目录
接口与API设计
用前缀避免命名空间冲突
提供“全能初始化方法”
实现description方法
尽量使用不可变对象
使用清晰而协调的命名方式
方法命名
编辑类与协议命名
为私有方法名加前缀
理解OC错误模型
理解NSCopying协议
协议与分类
通过委托与数据源协议进行对象间通信
将类的实现代码分散到便于管理的数个分类之中
总是为第三方类的分类名称加前缀
使用"class-continuation分类"隐藏实现细节
通过协议提供匿名对象
接口与API设计
用前缀避免命名空间冲突
为了避免“重名符号错误”,应当为所有名称都加上适当前缀,可以与公司、程序或二者皆有关。
但使用Cocoa创建程序时,要注意Apple保留使用所有“两字母前缀”的权利,所以开发者使用的前缀应该是三个字母的。
不仅是类,还有C函数的名字也应加上前缀
如果是开发第三方库,那么对于所有包含的第三方库代码都加上自己的前缀。
提供“全能初始化方法”
可以为对象提供必要信息以便其能完成工作的初始化方法叫做“全能初始化方法”。
在上图方法中,initWithTimeIntervalSinceReferenceDate:便是全能初始化方法。
当一个类有多个初始化方法时,仍要选定一个作为全能初始化方法,其他初始化方法都调用它。
只有在全能初始化方法中,才会存储内部数据,这样的话,当底层数据存储机制改变时,只需修改此方法的代码就好。
如果子类的全能初始化方法与超类方法的名称不同,那么总应覆写超类的全能初始化方法。
实现description方法
在自定义类中,description的默认实现是打印类名和地址,输出信息如下:
这时我们应尽量将输出信息改为一个有意义的字符串,用来描述这个实例。有个简单的方法,就是借助NSDictionary的description方法。此方法输出的信息的格式如下:
字符串可以采用如下格式:
若想在调试时打印更详尽的描述信息,应实现debugDescripption方法。
尽量使用不可变对象
在编程实践中,应该尽量把对外公布出来的属性设为只读,而只在确有必要时才将属性对外公布。
有时可能想修改封装在对象内部的数据,但是不想让这些数据为外人所改动,这种情况下通常做法是将readonly重新声明为readwrite,这一操作应该在"class-continuation分类"中完成。
在定义类的公共API时,还应注意:对象里表示各种collection的那些属性应该设成可变的,还是不可变的。
比如:我们用某个类来表示个人信息,该类中还存放了一些引用指向此人的朋友,开发者可以添加或删除朋友,那这个属性就要用可变的set来实现。这种情况下,通常应该提供一个readonly属性供外界使用,返回一个不可变的set,这个set是内部可变set的一份拷贝。比如下面这份代码:
使用清晰而协调的命名方式
OC当中方法与变量名采用“驼峰式大小写命名法”,而类名首字母大写,并且有两三个前缀字母。
方法命名
命名方法时,应当使方法像个日常用语中的句子,准确传达方法所执行的任务,然而方法名不能长得太过分,应尽量言简意赅。
类与协议命名
类与协议的名称应加上前缀,并且应该像给方法起名一样组织好词句,使从左至右比较通顺。
为私有方法名加前缀
在编写私有方法时,最好使用前缀将私有方法标注出来,前缀最好包含下划线与字母p。
要注意不应该直接使用下划线作为私有方法的前缀,因为苹果公司喜欢单用一个下划线作为私有方法的前缀,使用一个下划线有可能会无意中覆写超类中的其他方法。
理解OC错误模型
自动引用计数不是“异常安全”的,并且即使不用ARC,也很难写出在抛出异常时不会导致内存泄漏的代码。所以OC语言当中,异常只用于极其严重的错误,抛出异常之后,应用程序应该退出,也就无需考虑恢复问题了。
在出现不那么严重的错误时,OC语言所用的编程范式为:令方法返回nil/0,或是使用NSError,以表明其中有错误发生。
NSError的用法更加灵活,NSError对象里封装了三条信息:
Error domain:错误发生的范围,也就是产生错误的根源。
Error code:独有的错误代码,用以指明在某个范围内具体发生了何种错误。
User info:有关此错误的额外信息。
NSError有两种常见用法,一种是通过委托协议来传递错误,另一种是经由方法的“输出参数”返回给调用者。
理解NSCopying协议
OC中如果想令自己的类支持拷贝操作,那就要实现NSCopying协议,该协议只有一个方法:
这里zone参数不用考虑,使用默认参数即可。比如游客表示个人信息的类,可以声明遵从NSCopying协议:
有时需要获取可变的拷贝,则应遵守NSMutableCopying协议,该协议也只定义了一个方法:
当对象需要深拷贝时,可考虑吧新增一个专门执行深拷贝的方法
协议与分类
通过委托与数据源协议进行对象间通信
OC开发中经常使用一种“委托模式”,主旨是:定义一套接口,某对象若想接受另一个对象的委托,则需遵从此接口,以成为其“委托对象”,而这另一个对象,则可以给其委托对象回传一些信息,也可以在发生相关事件时通知委托对象。一般通过协议来实现委托模式。
用一张图来演示委托模式的概念:
这里EOCDataModel1就是作为EOCNetwirkFetcher的委托对象。
但是要注意,类中存放委托对象的属性需定义成weak,而非strong:
@interface EOCNetworkFetcher : NSObject
@property (nonatomic, weak) id <EOCNetworkFetcherDelegate> delegate;
@end
因为通常delegate要持有本对象,若本对象也持有delegate,那么就会引入保留环。
实现委托对象可以在接口中声明,也可以在“class-continuation分类”中声明。如果要向外界公布此类实现了某协议,那么就在接口中声明,如果是委托协议,通常只在类内部使用,一般在"class-continuation分类"里声明。
还有另一种,令某类经由协议中接口获取所需的数据,被称为“数据源模式”。
若有必要,可实现含有位段的结构体,将委托对象是否能相应相关协议方法这一信息缓存至其中。
将类的实现代码分散到便于管理的数个分类之中
当类中存在大量方法的代码时,可以通过OC的分类机制,把类代码按逻辑划入几个分区中。
比如下面这个管理个人信息的类:
可以把不同的方法放入不同分类中:
这些分类可以全部放在一个实现文件中,但当存在许多分类时,最好每个分类提取到各自的文件中去。以EOCPerson为例,可以拆分成下列这几个文件:
私有方法应归入名叫Private的分类中,以隐藏实现细节。
总是为第三方类的分类名称加前缀
我们经常通过分类为无源码的既有类添加方法,这时就容易出现命名冲突的问题。我们应该为分类和方法添加了前缀。
比如为NSString添加分类处理HTTP URL有关的字符串。
我们为其加上前缀:
使用"class-continuation分类"隐藏实现细节
class-continuation分类"和普通的分类不同,他必须定义在其所接续的那个类的实现文件里,这是唯一能声明实例变量的分类。
这种分类在实现文件中格式如下:
通过这种分类可以获得隐藏程度更好的私有方法和私有变量。
除了获得隐藏变量和方法之外,使用这种分类还可以将只读的属性扩展为可读写的,以便在类的内部设置其值。
还有一种用法是,当对象所遵从的协议只应视为私有,则可在该分类中声明
通过协议提供匿名对象
有时,我们可以把返回的对象设计为遵从某协议的id类型,这样的话,想要隐藏的类名就不会出现在API之中了。若接口背后有多个不同的实现类,又不想指明具体使用哪个,可以考虑用这个方法,此概念被称为“匿名对象”。
有时对象类型不重要,重要的是有没有实现某些方法,在此情况下,也可以使用这些“匿名类型”来表达这一概念。
比如对受委托者的定义: