开发该选择Blocks还是Delegates

前文:网络上找了很多关于delegation和block的使用场景,发现没有很满意的解释,后来无意中在stablekernel找到了这篇文章,文中作者不仅仅是给出了解决方案,更值得我们深思的是作者独特的思考和解决问题的方式,因此将这篇文章翻译过来,和诸君探讨,翻译的很多地方不是很到位,望大家提出意见建议。

原文链接:http://blog.stablekernel.com/blocks-or-delegates/

By Joe Conway,CEO | Jul 11,2013 11:29:00 AM


有人问了我一个很棒的问题,我把这个问题总结为:“开发过程中该选择 blocks or delegates?当我们需要实现回调的时候,使用哪一种方式比较合适呢?”

一般在这种情况下,我喜欢问我自己:“如果问题交给Apple,他会怎么做呢?”当然,我们都知道Apple肯定知道怎么做,因为从某一层面上看,Apple的文档就是一本用来指导我们如何使用设计模式的指导书。

因此我们需要去研究一下Apple分别是在什么情况下使用delegate和block,如果我们发现了Apple做这种选择的套路,我们就可以构建出一些规则,可以帮助在我们在自己的代码中做相同选择。

要找出Apple使用delegate的场景很简单,我们只要搜索官方文档中的“delegate”,就会获取到很多使用delegation的类。

但是搜索Apple中有关使用blocks的文档就有点困难了,因为我们不能直接搜索文档中的“^” 。然而,Apple声明方法时有很好的命名习惯(这也是我们精通iOS开发的一项必备技能)。例如:一个以NSString为参数的方法,方法的selector就会有String字眼,像initWithString;dateFromString;StartSpeaingString

当Apple的方法使用block,这个方法将会有“Handler”,“Completion”或者简单的“Block”作为selector;因此我们可以在标准的iOS API文档中搜索这些关键词,用以构建一个可信任的block用例列表。

1.大多数delegate protocols 都拥有几个消息源。

以我正在看的GKMatch为例(A GKMatch object provides a peer-to-peer network between a group of devices that are connected to Game Center,是iOS API中用来提供一组设备连接到Game Center点对点网络的对象)。从这个类中可以看到消息的来源分别是:当从其他玩家那接收到数据、当玩家切换了状态、当发生错误或者当一个玩家应该被重新邀请。这些都是不同的事件。如果Apple在这里使用block,那么可能会有以下两种解决方式:

可以对应每一个事件注册相应的block,显然这种方式是不合理的。( If someone writes a class that does this in Objective-C, they are probably an asshole.)
创建一个可以接受任何可能输入的block
<pre><code>void?(^matchBlock)(GKMatchEvent?eventType,?Player?player,?NSData?data,?NSError?*err);</pre></code>
很明显这种方式既不简便又不易读,所以你可能从未看过这样的解决方案。如果你看过这样的解决方式,但是这显然是一个糟糕至极的代码行,你不会有精力去维护这个。

因此,我们可以得出一个结论:如果对象有超过一个以上不同的事件源,使用delegation。

2.一个对象只能有一个delegate

由于一个对象只能有一个delegate,而且它只能与这个delegate通信。让我们看看CLLocationManager 这个类,当发现地理位置后,location manager 只会通知一个对象(有且只有一个)。当然,如果我们需要更多的对象去知道这个更新,我们最好创建其他的location manager。

这里有的人可能想到,如果CLLocationManager是个单例呢?如果我们不能创建CLLocationManager的其他实例,就必须不断地切换delegate指针到需要地理数据的对象上(或者创建一个只有你理解的精密的广播系统)。因此,这样看起来,delegatetion在单例上没有多大意义。

关于这点,最好的印证例子就是UIAccelerometer。在早期版本的iOS中,单例的 accelerometer 实例有一个delegate,导致我们必须偶尔切换一下。这个愚蠢的问题在之后的IOS版本被修改了,现在,任意一个对象都可以访问CMMotionManager block,而不需要阻止其他的对象来接收更新。

因此,我们可以得出另一个结论:“如果一个对象是单例,不要使用delegation”。

3.一般的delegate方法会有返回值

如果你观察一些delegate方法(几乎所有的dataSource方法)都有一个返回值。这就意味着delegating对象在请求某些东西的state(对象的值,或者对象本身),而一个block则可以合理地包含state或者至少是推断state,因此block真正是对象的一个属性。

让我们思考一下一个有趣的场景,如果向一个block提问:“What do you think about Bob?”。block可能会做两件事情:发送一个消息去捕获对象并询问这个对象怎么看待Bob,或者直接返回一个捕获的值。如果返回了一个对象的响应,我们应该越过这个block直接获取这个对象。如果它返回了一个捕获的值,那么这应该是一个对象的属性。

从以上的观察,我们可以得出结论:如果对象的请求带有附加信息,更应该使用delegation

4.过程 vs 结果(Process vs. Results)

如果查看NSURLConnectionDelegate 以及 NSURLConnectionDataDelegate,我们在可以protocol中看到这样的消息:我将要做什么(如: willSendRequest,将要发送请求)、到目前为止我知道的信息(如:canAuthenticateAgainstProtectionSpace)、我已经完成这些啦( didReceiveResponse,收到请求的回复,即完成请求)。这些消息组成一个流程,而那些对流程感兴趣的delegate将会在每一步得到相应的通知。

当我们观察handler和完整的方法时,我们发现一个block包含一个响应对象和一个错误对象。显然这里没有任何有关“我在哪里,我正在做什么的”的交互。

因此我们可以这样认为,delegate的回调更多的面向过程,而block则是面向结果的。如果你需要得到一条多步进程的通知,你应该使用delegation。而当你只是希望得到你请求的信息(或者获取信息时的错误提示),你应该使用block。(如果你结合之前的3个结论,你会发现delegate可以在所有事件中维持state,而多个独立的block确不能)

从上面我们可以得出两个关键点。首先,如果你使用block去请求一个可能失败的请求,你应当只使用一个block。我们可以看到如下的代码:
<pre><code>
[fetcher makeRequest:^(id result) {
// do something with result
} error:^(NSError *err) {
// Do something with error
}];
</pre></code>

上面代码的可读性明显比下面block的可读性差(作者说这个是他不谦虚的观点,其实个人认为没有那么严重)
<pre><code>
[fetcher makeRequest:^(id result, NSError *err) {
if(!err) {
// handle result
} else {
// handle error
}
}];
</pre></code>

转载自:http://www.cocoachina.com/ios/20150927/13525.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容

  • 基础 1. 为什么说Objective-C是一门动态的语言? 2. 讲一下MVC和MVVM,MVP? 3. 为...
    波妞和酱豆子阅读 3,307评论 0 46
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,124评论 29 470
  • 1.属性readwrite,readonly,assign,retain,copy,nonatomic 各是什么作...
    曾令伟阅读 1,044评论 0 10
  • 禅与 Objective-C 编程艺术 (Zen and the Art of the Objective-C C...
    GrayLand阅读 1,601评论 1 10
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139