Effective Objective-C - 协议与分类
前言
Effective Objective-C 读书笔记 - 协议与分类
协议(protocol)与Java的接口(interface)类似,由于OC不支持多重继承。通过协议,可以实现类似多重继承的效果。若指定某个类实现了某个协议,那么就表示,该类实现了协议中规定了一些方法。
分类(category)是OC的重要的一个语言特性。利用分类机制,无须继承子类即可为当前类添加方法。这也是由于动态runtime系统,才得以实现。
笔记
通过委托与数据源协议进行对象间通信
注意一点,声明delegate的时候,要声明成weak类型的。避免循环引用。
通常将:委托对象是否可以响应协议中的某个方法 这一信息缓存起来。之后就不需要一直查询该对象是否响应该方法了。缓存可以使用位段来实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15@interface Example() {
struct {
unsigned int didReceiveData : 1;
unsigned int didFailWithError : 1;
} _delegateFlags;
}
@end
@implementation Example
- (void)setDelegate:(id<ExampleDelegate>)delegate {
_delegate = delegate;
_delegateFlags.didReceiveData = [delegate respondsToSelector:@selector(didReceiveData:)];
_delegateFlags.didFailWithError = [delegate respondsToSelector:@selector(didFailWithError:)];
}
@end在调用delegate时,就不需要判断delegate是否响应某方法了,只需要看_delegateFlags里面的字段就可以了
将类的实现代码分散到便于管理的数个分类之中
- 将类的方法分散到几个分类中
- EOCPerson+Friendship(.h/.m)
- EOCPerson+Work(.h/.m)
- EOCPerson+Play(.h/.m)
- 可以创建一个Private的分类,将所有的‘私有方法’写在里面,这个private的头文件可以被其他文件所引用,但是发布的时候,不会公布出去。所以只能在程序内部使用,第三方使用者无法使用private分类中的方法。
总是为第三方类的分类名称加前缀
分类机制通常用于向五元吗的既有类中新增功能。
分类的方法相当于直接添加在既有类中的,他们好比这个类的固有方法
将分类中的方法加入既有类的方法列表是在runtime加载分类的时候。这就有一个问题了,如果分类中的方法名和既有类原始方法同名,那么会覆盖原始方法的实现
基于上述问题,就要加前缀
1
2
3@interface NSString (ABC_HTTP)
- (NSString *)abc_method;
@end
勿在分类中声明属性
- 属性包含实例变量,分类无法自动合成属性的实例变量。
- 要想硬添加属性也是可以的。手动添加合成实例变量的方法,也就是实现该属性的存取方法,也就是调用
objc_getAssociatedObject
和objc_setAssociatedObject
方法
使用class-continuation分类隐藏实现细节
这种分类与普通的分类不同。其实我们一直在使用这个分类。。。
这个分类是唯一能正常的声明实例变量的分类,他必须写在实现文件中
1
2
3
4
5
6@interface EOCPerson() {
NSString *_instanceVariable; // 实例变量
}
@property (nonatomic, strong) NSString *pro; // 属性
- (void)p_someMethod; // 声明私有方法
@end虽然不要求声明私有方法,但是最好这样做
如果只是私有的遵循某协议的话,也需放在该分类中
1
2@interface EOCPerson () <EOCSecretDelegate>
@end
通过协议提供匿名对象
id<EOCPersonDelegate> delegate;
这个delegate就是匿名对象,不需要关心对象是什么类型,只要他遵循该协议就可以了使用这个还可以返回多个基类不是相同的类,如下:
1
2
3
4
5
6
7
8
9
10
11@protocal EOCDBConnection
- (void)connect;
- (void)disconnect;
- (BOOL)isConnect;
- (NSArray *)performQuery:(NSString *)query;
@end
@interface EOCManager : NSObject
+ (id)sharedInstance;
- (id<EOCDBConnection>)connectionWithIdentifier:(NSString *)identifier;
@end上面这个例子用来实现数据库的连接,不管是连接何种类型的数据库(Mysql,SQLite,PostreSQL)等等,都可以使用
connectionWithIdentifier:
方法。这个方法底层的实现:根据不同的identifier,选择性的返回与连接 Mysql或是SQLite或是PostreSQL 相关的对象,可能返回的对象叫做 MySQLManager, SQLiteManager, PostreSQLManager。 这些对象因为可能是来自3种第三方库的,所以不会有相同的基类,但是只要他们实现了delegate的方法,都可以这样返回。
这样做:既保证了接口的简介,又隐藏了底层的实现