(六) 协议与分类

第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的名称和细节,也不关心。匿名对象表明具体类型并不重要,仅需要能响应协议方法。在程序设计中,同一套协议接口,可能会由不同的对象来实现。数据持久化常使用匿名对象,如数据库、文件处理、缓存等等。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容