文章目录
第15条:用前缀避免命名空间冲突
为什么要避免命名空间冲突?
Objective-C没有其他语言那种内置的命名空间 (namespace) 机制.在应用开发时,所有的代码和引用的静态库最终都会被编译到同一个域和二进制中。这样的后果是一旦我们有重复的类名的话,就会导致编译时的冲突和失败。
避免命名冲突
- 变相实现命名空间:为所有名称都加上适当前缀。
- 使用Cocoa创建应用程序时一定要注意,Apple宣称其保留使用所有“两字母前缀”(two-letter-prefix)的权利,所以我们选用的前缀最好是三个字母的。
- 不仅是类名,应用程序中的所有名称都应加前缀,包括分类以及分类方法。
- 在类的实现文件中,所用的纯C函数以及全局变量,在编译好的目标文件中,是要算作“顶级符号”(top-level-symbol)的。
- 可以参考之前static(当static关键字修饰局部变量时,只会初始化一次。),如果不加static,在两个.m文件中声明同一个string 变量,会冲突报错。
- 使用第三方库A编写自己的代码,并准备将其发布为程序库B供他人开发程序所用,此时应该给所用的那一份第三方库A代码都加上自己的前缀。(现在一般使用Cocoapods来管理第三方,这种第三方库冲突就很少见了)
要点
-
选择与公司、应用程序或二者皆有关联之名称作为类名的前缀,并在所有代码中均使用这一前缀。
-
若自己所开发的程序库中用到了第三方库,则应为其中的名称加上前缀。
第16条:提供“全能初始化方法”
所有对象均要初始化。
可为对象提供必要信息以便其能完成工作的初始化方法叫做“全能初始化方法”(designated initializer, 也常译为:“指定初始化方法”)。
初始化方法可以调用全能初始化方法来实现(一个类可以有多个)。于是,只有在全能初始化方法中,才会存储内部数据。这样的话,当底层数据存储机制改变时,只需要修改此方法就好,无需改动其他初始化方法。
要点:
-
在类中提供一个全能初始化方法,并于文档里指明。其他初始化方法均应调用此方法。
-
全能初始化方法的调用链一定要维系。每个子类的全能初始化方法都应该调用其超类的对应方法,并逐层向上。
-
若全能初始化方法与超类不同,则需覆写超类中的对应方法。
-
如果超类的初始化方法不适用于子类,那么应该覆写这个超类方法,并在其中抛出异常。(超类的初始化方法是为子类服务的?)
第17条:实现description方法
什么是description方法?
description是所有类都有的一个方法。
我们重写这个方法,可以自定义实例输出的信息。
比如我们可以创建一个Person类:
在.h文件中添加两个属性
#import
@interfacePerson : NSObject
@property (strong, nonatomic) NSString*name;
@property (assign, nonatomic)intage;
@end
在.m文件中重写description方法:
#import"Person.h"
@implementationPerson
- (NSString *)description
{
return[NSString stringWithFormat:@"<%p> - name: %@, age: %d", self, _name, _age];
}
@end
通过主函数进行调用
Person *person =[[Person alloc] init];
person.name=@"XiaoMing";
person.age=28;
NSLog(@"person - %@", person);
此时控制台会有输出:
person - <0x7fa20bc18d10> - name: XiaoMing, age:28
如果在.m文件中没有重写description方法,则输出结果为:
person - <0x7fa20bc18d10>
此方法定义在NSObject协议里,不过NSObject类也实现了它。因为NSObject并不是唯一的“根类”,所以许多方法都要定义在NSObject协议里。比方说,NSProxy也是一个遵从了NSObject协议的“根类”。由于description等方法定义在NSObject协议里,因此像NSProxy这种“根类”及其子类也必须实现它们。
这就是这个方法的作用 所以当我们在自己的类中想要输出信息,就要覆写description方法
要点:
- 实现description方法返回一个有意义的字符串,用以描述该实例。
- 若想在调试时打印出更详尽的对象描述信息,则应实现debugDescription方法。
第18条:尽量使用不可变对象
- 声明对外属性时,尽量使用不可变对象,同时,对外属性声明里尽量加上readonly修饰(默认是readwrite修饰)。这样外部只能读取数据而不能修改数据,保证了这个类的实例所持有的数据更加安全。尤其是不要把可变的collection作为属性公开,而是应该提供相应的方法修改可变的collection。
- 若外部想修改修改对象的值有两种途径:
- 提供接口方法修改
- 使用KVC(Key-Value Coding)技术
这种技术允许对象的数据或属性可以在运行时通过其键名进行查找,其中,属性的名称即为其值的键名。在静态语言中,这样的做法是不可能的。KVC大大的增加了设计的自由度:通过KVC,无需知道对象的类型即可访问其属性或数据。
例如 :
不推荐写法:
//Animals.h
@property (nonatomic, strong) NSSet *animals;
应改为:
//Animals.h
@interface Animals : NSObject
@property (nonatomic, strong, readonly) NSSet *animals;
- (void)addAnimal:(NSString *)animal;
- (void)removeAnimal:(NSString *)animal;
@end
//Animals.m
@implementation Animals {
NSMutableSet *_mutableAnimals;
}
- (NSSet *)animals {
return [_mutableAnimals copy];
}
- (void)addAnimal:(NSString *)animal {
[_mutableAnimals addObject:animal];
}
- (void)removeAnimal:(NSString *)animal {
[_mutableAnimals removeObject:animal];
}
这样写固然有好处:保证了数据的安全性,但代码量也会提升不少。所以推荐大家可以有选择的使用,对一些重要的类才有使用必要。
要点
-
声明对外属性时,尽量创建不可变的对象
-
若某属性仅可于对象内部修改,则在“class-continuation分类”中将其由readonly属性扩展为readwrite属性也可以在.h文件中把属性声明为readonly,在.m文件中通过实例变量修改值,当block内部修改值时,可以用self->实例变量的方法访问修改。
-
不要把可变的collection作为属性公开,而应提供相关方法,以此修改对象中的可变collection
第19条:使用清晰而协调的命名方式
方法与变量名整体上的规则
- 方法与变量名使用了“驼峰命名”,以小写字母开头,其后每个单词首字母大写。类名也用驼峰命名发,不过其首字母需要大些。
方法命名时的规则
- 如果方法的返回值是新创建的,那么方法名的首个词应是返回值类型,除非前面还有修饰语,例如localizedString。属性的存取方法不遵循这种命名方式,因为一般认为这些方法不会创建新的对象,即便有时返回内部对象的一份拷贝,我们也认为那相当于原有的对象。
- 应该把表示参数类型的名词放在参数前面。
- 如果方法要在当前对象上执行操作,那么就应该包含动词;若执行操作时还需要参数,就在动词后面加上一个或多个名词。
- 不要使用str这种简称
- Boolean属性应该加is前缀。如果某方法返回非属性的Boolean值,那么应该根据其功能,选用has或is前缀。
- 由“输入输出”来保存返回值的方法用get前缀。
类与协议的命名
命名方式应该协调一致。如果从其他框架中继承子类,那么务必遵循其命名惯例,例如,继承自UIView的自定义子类,那么累名末尾的词必须是view
要点
- 起名时应遵从标准的Objective-C命名规范,这样创建出来的接口更容易为开发者所理解。
- 方法名要言简意赅。
- 方法名里不要使用缩略后的类型名称。
第20条:为私有方法名加前缀
一个类所做的事情通常都要比从外面看到的更多。编写类的实现代码时,经常要写一些只在内部使用的方法。应该为这些方法的名称加上某些前缀,这样有如下好处
- 有助于调试,因为据此可以很容易地把公有方法和私有方法区分。
- 便于修改方法名或方法签名。对于公共方法来说,修改了其名称或签名,那么使用这个类的所有其他代码都得更新才行。而对于内部方法来说,若要修改其签名或名称,则只需同时修改本类内部的相关代码即可,不会影响面向外界的API。
要点:
-
给私有方法的名称加上前缀,可以区分公有方法和私有方法。
-
不要单用一个下划线作为私有方法的前缀。因为这种做法是预留给苹果公司的。
第21条:理解Objective-C错误模型
后续有博客 关于ARC与错误模型的
要点
- 只有发生了导致整个程序崩溃的错误时,才使用异常
- 在错误不是很严重的情况下可以使用“委托方法”来处理错误,也可以把错误信息放在NSError对象中,经由“输入输出”返回给调用者。