Farlanki.

block和delegate作为回调

字数统计: 1.2k阅读时长: 4 min
2016/02/27 Share

http://blog.stablekernel.com/blocks-or-delegates/
block和delegate,究竟什么时候选择哪种方式作为回调呢?
当遇到这样的问题时,或许最好的答案是参考Apple的做法.找出Apple在什么情况下使用delegate十分简单:在文档中搜寻delegate,然后我们会找到大部分使用了delegate的类.

相对而言,找出Apple在什么情况下使用了block的难度会大一点,因为我们不能再文档中寻找^符号.然而,Apple在遵守命名规则这方面做得很好.例如,一个将NSString类型的对象作为参数的方法的名字中会出现”String”,就像initWithString:.dataFromString.

当一个Apple的方法需要一个block作为参数,这个方法的名字将会包含Handler,Completion或者Block.我们可以在文档中搜寻这些关键字,并且得到block的常用方法.

大部分协议包含多个消息

观察GKMatch类.我们可以发现一下各种情况下都有对应的消息:当接收到另一个播放器的数据,当播放器的状态改变了,当一个错误发生了.这些都是直接的事件.如果Apple在这时使用block,将会有两个选择.第一为每个事件都注册一个block(播放器状态改变block,播放器发生错误blcok等).第二个选择是创建一个接收所有可能输出的block:

1
void (^matchBlock)(GKMatchEvent eventType, Player *player, NSData *data, NSError *err);

这种方法既不方便又不直观.所以这种情况永远都不会出现.

每个对象只能有一个同类协议

因为一个对象只能有一个同类协议,它只能对该协议传递消息.观察CLLocationManager,当这个类找到一个地点时,这个类会告诉一个对象.当然,如果我们需要多于一个对象知道这些信息,我们可以创建另一个location manager.
如果CLLocationManager是一个单例呢?如果我们不能创建更多的CLLocationManager实例,我们必须不停地把协议在需要地点信息的对象中各个类中交换.所以,在一个单例中使用协议不是很合理.
UIAcclermeter是一个很好的例子.在iOS的早期版本中,使用单例模式的accelerometer的实例有一个需要不时地转换的协议.因为这个机制十分麻烦,所以在后来的iOS版本中,这个机制被更改了.现在,每个类都可以在CMMotionManager类中添加block,并且不堵塞其他类接收消息.所以,可以得出一个结论:如果使用了单例,慎重使用协议.

一些协议方法要有返回值

一些协议方法要有返回值,这意味着发出委托的方法需要某些东西的状态.当然,一个block可以保存状态值,或者推断状态值,但是,这更像是一个对象应该做的事,而不是一个block.
想象这样一种情况:如果我们问一个block,”你觉得Bob这个人怎样?”,这个block只能做两件事:一是向被捕获的对象发送消息并且向该对象询问该对象对Bob的看法,第二是返回一个被捕获的值.如果这个block返回的是一个对象的看法,为何不绕过这个block,直接问这个对象呢?如果这个block返回的是一个捕获了的值,为什么不直接从对象得到呢?(这个值很可能是对象的属性).
所以,可以得出一个结论,如果一个回调需要额外的信息,可以考虑使用协议.

过程VS结果

如果我们观察NSURLConnectionDelegateNSURLConnectionDataDelegate,我们可以看到想表达如下内容的消息”我将要开始做这件事了”,”这是我到目前为止所知道的”,”我已经做完这件事了”或者”析构!!!”.这些消息为我们画出了这个对象处理过程的轮廓.
当我观察handlercompletion方法,我看到的是一个包含了错误信息或者结果的block.这里边并没有任何像”我将要开始做这件事了”的交流.
所以,我们可以得出一个结论:delegate回调更加偏向于面对过程,而block回调更加偏向于面对结果.如果你需要在一个过程的每一步都进行通知,那么使用协议.如果你只是想传递一个结果,那么使用block.
这里是亮点建议.如果需要使用block来进行一个可能会失败的请求,应该使用一个block

1
2
3
4
5
[fetcher makeRequest:^(id result) {
// do something with result
} error:^(NSError *err) {
// Do something with error
}];

下面这种方法的可读性就好了很多

1
2
3
4
5
6
7
[fetcher makeRequest:^(id result, NSError *err) {
if(!err) {
// handle result
} else {
// handle error
}
}];
CATALOG
  1. 1. 大部分协议包含多个消息
  2. 2. 每个对象只能有一个同类协议
  3. 3. 一些协议方法要有返回值
  4. 4. 过程VS结果