有人问了我一个很好的问题。我把这个问题总结为:“我们在开发中应该选择块还是委托?当我们需要实现回调时,我们应该使用哪个?”有没有更合适的方式?”一般遇到这种情况,我喜欢问自己:“如果把问题交给苹果,他会怎么做?”当然,我们都知道苹果肯定知道怎么做,因为从某种层面上看,苹果的文档是指导我们如何使用设计模式的指南书。因此,我们需要研究一下苹果分别在什么情况下使用delegate和block。如果弄清楚苹果是如何做出这种选择的,我们可以在自己的代码中建立一些可以帮助我们做出相同选择的规则。。找出Apple使用delegate的场景非常简单。我们只需要在官方文档中搜索“delegate”,我们就会得到许多使用委托的类。但是搜索Apple关于使用块的文档有点困难,因为我们不能直接在文档中搜索“^”。但是Apple在声明方法时有很好的命名约定(这也是必须的-有能力让我们精通iOS开发分)。例如:一个以NSString为参数的方法,方法的selector会有String这个词,如initWithString;dateFromString;StartSpeaingString。当Apple方法使用块时,该方法将使用“Handler”、“Completion”或简单的“Block”作为选择器;所以我们可以在标准的iOSAPI文档中搜索这些关键字来构建一个可信的块用例列表。1.大多数委托协议都有多个信息源。以我正在查看的GKMatch为例(GKMatch对象在连接到GameCenter的一组设备之间提供对等网络,这是iOSAPI中用于提供一组设备的对象连接到GameCenter点对点网络)。从这个类中,您可以看到消息的来源:何时从另一个玩家接收到数据,何时玩家切换状态,何时发生错误或何时应重新邀请玩家。这些是不同的事件。如果Apple这里使用block,可能有以下两种解决方案:可以为每个事件对应注册相应的block,这显然是不合理的。(如果有人在Objective-C中编写了一个这样做的类,他们可能是个混蛋。)创建一个block1void(^matchBlock)(GKMatchEventeventType,Player*player,NSData*data,NSError*err)输入;显然这既不方便也不可读,因此您可能从未见过这样的解决方案。如果你见过这样的变通方法,但它显然是一行非常糟糕的代码,你将没有精力去维护它。因此,我们可以得出一个结论:如果对象有多个不同的事件源,则使用委托。2、一个对象只能有一个委托因为一个对象只能有一个委托,它只能和这个委托通信。让我们看一下CLLocationManager类。当找到一个位置时,位置管理器只会通知一个对象(只有一个)。当然,如果我们需要更多的对象来了解这个更新,我们最好创建其他位置管理器。这里可能有人会想,如果CLLocationManager是单例呢?如果我们无法创建CLLocationManager的其他实例,我们将不得不不断将委托指针切换到需要地理数据的对象(或者创建一个只有您了解的复杂广播系统)。因此,委托在单例上似乎意义不大。关于这一点,最好的证明例子就是UIAccelerometer。在早期版本的iOS中,单例加速度计实例有一个委托,导致我们不得不偶尔切换它。这个愚蠢的问题在后来的IOS版本中得到了修复,现在,任何对象都可以访问CMMotionManager块,而不会阻止其他对象接收更新。因此,我们可以得出另一个结论:“如果对象是单例,就不要使用委托”。3.一般的委托方法都会有返回值如果你观察到一些委托方法(几乎所有的dataSource方法)都有返回值。这意味着委托对象正在请求某物的状态(对象的值,或对象本身),并且块可以合理地包含状态或至少推断状态,因此块实际上是对象的属性。让我们考虑一个有趣的场景,如果你问一个街区:“你觉得Bob怎么样?”。该块可以做两件事:向捕获对象发送消息并询问该对象对Bob的看法,或者简单地返回一个捕获值。如果返回对象响应,我们应该绕过这个块,直接获取对象。如果它返回一个捕获的值,那么这应该是对象的一个??属性。从上面的观察,我们可以得出一个结论:如果对象的请求有额外的信息,我们应该使用delegation4。Processvs.Results(过程与结果)如果我们查看NSURLConnectionDelegate和NSURLConnectionDataDelegate,我们可以在协议中看到这样的消息:WhatwillIdo(如:willSendRequest,请求将被发送),目前我知道的信息(如:canAuthenticateAgainstProtectionSpace),而我已经完成了这些(didReceiveResponse,收到请求的响应,请求完成)。这些消息形成一个流程,那些对流程感兴趣的代表将在每一步得到相应的通知。当我们查看处理程序和完整方法时,我们发现一个块包含一个响应对象和一个错误对象。显然这里没有任何“我在哪里,我在做什么”的交互。因此,我们可以认为delegate的回调更面向过程,而block则是面向结果的。如果需要通知多步骤流程,则应使用委托。而当你只是想获取你请求的信息时(或者获取信息时出现错误信息),你应该使用块。(如果结合前面3个结论,你会发现delegate可以在所有事件中保持状态,但是多个独立的block不可以)从上面我们可以得出两个关键点。首先,如果你使用块来请求一个可能会失败的请求,你应该只使用块。我们可以看到如下代码:12345[fetchermakeRequest:^(idresult){//dosomethingwithresult}error:^(NSError*err){//Dosomethingwitherror}];上面代码的可读性明显比下面这个block的可读性要差(作者说这是他不谦虚的观点,其实我个人认为没那么严重)1234567[fetchermakeRequest:^(idresult,NSError*err){if(!err){//处理结果}else{//处理错误}}];
