第23条:通过委托与数据源协议进行对象间通信。
Objective-C广泛使用委托模式(Delegate patttern)来实现对象间的通信,其主旨是:为对象A定义一套接口,对象B接受对象A的委托,需要遵从这套接口。通过接口,对象A在发生事件时可以通知对象B,也可以向对象B传递信息。接口通常以协议(protocol)方式呈现。
委托模式可将数据与业务逻辑解耦。比如,UI视图只应包含显示数据逻辑,其数据交互处理可交由委托对象处理。视图一般包含负责数据的数据源(data source)对象和负责事件的委托(delegate)对象。为避免循环引用,视图对委托属性必须是非拥有关系,需要使用weak关键字。
协议中使用@optional关键字表示委托对象可选择是否实现方法,调用该方法前需要先用respondsToSelector:来检测方法是否已实现,判断结果特定场景下可缓存起来,减少检测次数。
协议中必须实现的方法通常用于强制实现的接口规范,可直接调用。
第24条:将类的代码分散到便于管理的多个分类中。
Objective-C分类机制,可以把类代码按逻辑划入几个分区中,利于开发和调试。类的基本要素,如属性、初始化方法等都在主实现(main implement)声明,不同类型的操作方法则归入各个分类中。
编写程序库时,可以考虑创建private分类,实现私有方法,隐藏实现细节。
第25条:为第三方类的分类及其方法名加上前缀。
因为Objective-C分类可以多个并存,为第三方类添加分类,使用简单名称,可能会存在同名分类和相同方法,导致其中一个分类方法被覆盖,有可能产生非预期结果。解决问题的建议是为分类及其方法名加上专用前缀,降低冲突几率。
第26条:分类中尽量不要声明属性。
分类无法合成与属性相关的实例变量,如果在分类声明属性,需要为该属性实现存取方法,可声明为@dynamic,到运行期才能提供,编译期不可见。
关联对象能够解决分类中不能合成实例变量的问题,需要遵从内存惯例语义。一般情况下并不推荐使用关联对象,因此,应尽量在主实现声明属性。
第27条:使用class-continuation分类隐藏实现细节。
class-continuation分类定义在类的实现文件中,用于隐藏实现细节,格式如下
@interface className () {
// 实例变量
}
// proterty
// method
@end
Objective-C可能包含C++代码,如果在头文件引入C++头文件,那么实现文件不能使使用.m扩展名,而要使用.mm扩展名。如果使用C++编写的第三库采用这种方式,则必须要求使用者的实现文件使用.mm扩展名,这样做不太合适。使用class-continuation分类添加C++的实例变量可以解决这个问题,只需要在实现文件引入C++头文件,对外提供的纯Objective-C接口,对于使用者来说比较友好。
class-continuation分类还有一种用法,是把在public接口声明为只读的属性,扩展为可读写。这样外部无法修改属性,内部比较容易管理数据。例如
头文件声明类
@interface EOCPerson : NSObject
@property (nonatomic, copy, readonly) NSString * firstName;
@end
实现文件可使用class-continuation分类进行扩展
@interface EOCPerson ()
@property (nonatomic, copy, readwrite) NSString * firstName;
@end
如果需要隐藏协议细节,也可以使用class-continuation分类,例如
@interface EOCPerson () <EOCSecretDelegate>
@end
第28条:通过协议提供匿名对象。
Objective-C可以用协议把API实现细节隐藏起来,对外仅提供遵从协议对id类型(匿名对象,anonymous object)。使用者不知道API的名称和细节,也不关心。匿名对象表明具体类型并不重要,仅需要能响应协议方法。在程序设计中,同一套协议接口,可能会由不同的对象来实现。数据持久化常使用匿名对象,如数据库、文件处理、缓存等等。