52个有效方法(23) - 通过委托与数据协议进行对象间的通信
委托模式(Delegate pattern)
委托模式(Delegate pattern):用来实现对象间的通信
委托模式的主旨
定义一套接口,某对象若想授受另一个对象的委托,则需遵从此接口,以便成为其“委托对象”(delegate)。而这“另一对象”则可以给其委托对象回传一些消息,也可以在发生相关事件时通知委托对象。
委托模式可以将数据与业务逻辑解耦
由数据源(data source)与委托(delegate)对象分别处理。
-
数据源:数据源流向类(输入),
-
委托:从类流向受委托者(输出)。
数据源和受委托者可以是两个不同的对象。然而一般情况下,都用同一个对象来扮演这两种角色。
在数据源模式中,信息从数据源(Data Source)流向类(Class);而在常规的委托模式中,信息则从类流向受委托者(Delegate)委托协议
在Objective-C中,一般通过协议(protocol)这项语言特性来实现此模式,它与Java的“接口”(interface)类似。Objective-C不支持多继承,但可以把类的实现方法定义在协议里面。
委托协议命名
-
类名+Delegate(后缀)
-
遵循驼峰命名法
-
delegate 方法一般以依靠 delegate 的类名开头,然后把依靠 delegate 的对象用第一个参数传进去。方法名一般常用will-、should-、did-这些词。比如,webViewDidStartLoad:(参数就是那个 webView)就要比loadStarted(没参数)好得多。
回调委托对象的流程
回调委托对象的流程回调委托对象的流程 :EOCDataModel
对象就是EOCNetworkFetcher
的委托对象。EOCDataModel
请求EOCNetworkFetcher
"以异步方式执行一项任务"(perform a task asynchronously),而EOCNetworkFetcher
在执行完这项任务之后,就会通知其委托对象EOCDataModel
。
有了协议后,类就可以用一个属性来存放其委托对象了。
@property(nonatomic, weak) id<类名+Delgate> delegate;
-
此处用weak表示“非拥有关系”(nonowning relationship)。
非拥有关系
-
还可以用unsafe_unretained。
-
weak与unsafe_unretained的区别:如果需要在相关对象销毁时自动清空(autoniling,参见第6条),则用weak。反之用unsafe_unretained。
@optional关键字(可选的)
在委托对象上调用可选方法时必须提前使用类型信息查询方法(参见第14条)来判断这个委托对象能否响应相关选择子。(注意:如果不是可选方法,则不需要。)
// 注意:如果不是可选方法,则不需要。
if([_delegate respondsToSelector:@selector(委托方法)]) {
[_delegate 委托方法];
}
选择子判断的优化
有时除了第一次检测的结果有用之外,后续的检测可能是多余的。对于频繁的检测,也消耗性能。
对此可以优化:把委托对象能否响应某个协议方法这一信息缓存起来。
将方法响应能力缓存起来的最佳人途径是使用“位段”(bitfield)数据类型。这是一项无人问津的C语言特性。
位段: 把结构体中某个字段所占用的二进制位个数设为特定的值。
struct data {
unsigned int fieldA: 8; // 表示0~255之间的值
unsigned int fieldB: 4;
unsigned int fieldC: 2;
unsigned int fieldD: 1; // 表示0或1
}
这样就可以嵌入一个含有位段的结构体作为实例变量,而结构体中的每个位段表示delegate对象是否实现了协议中的相关方法。(类似字典)
struct {
unsigned int didReceiveData: 1;
unsigned int didFailWithError: 1;
unsigned int didUpdateProgressTo: 1;
} _delegateFlags;
-(void)setDelegate:(id<协议> delegate){
_delegate = delegate;
_delegateFlags.didReceiveData = [@delegate respondsToSelector:@selector(方法)]; // 值为:0或1
// ……
}
if(_delegateFlags.didReceiveData) {
[_delegate 方法]
}
delegate对象可以有多个,所以在定义或调用时,应该把发起委托的类或实例加入进去。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if(tableView == tableViewA){
}else if(tableView == tableViewA){
}
}
关键字
-
weak
: 指明该对象并不负责保持delegate这个对象,delegate这个对象的销毁由外部控制。 -
strong
:该对象强引用delegate,外界不能销毁delegate对象,会导致循环引用(Retain Cycles)。 -
assign
:也有weak的功效。但assign是指针赋值,不对引用计数操作,使用之后如果没有置为nil,可能就会产生野指针;而weak会在销毁delegate指向的对象后,delegate置为nil,这样就没有野指针。
要点
-
委托模式为对象提供了一套接口,使其可由此相关事件告知其他对象。
-
将委托对象应该支持的接口定义成协议,在协议中把可能需要处理的事件定义成方法。
-
当某个对象需要从另一个对象中获取数据时,可以使用委托模式。这种情景下,该模式亦称“数据源协议”(data source protocal)。
-
若有必要,可实现含有位段的结构体,将委托对象是否能响应相关协议方法这一信息缓存至其中。