博客> delegate 与Block 的区别
delegate 与Block 的区别
2019-10-22 16:33 评论:0 阅读:768 cvl556
delegate block

delegate

委托是协议的一种,顾名思义,就是委托他人帮自己去做事。委托是给一个对象提供机会对另一个对象中的变化做出反应或者影响另一个对象的行为。其基本思想是:两个对象协同解决问题,并且打算在广泛的情形中重用。委托指向另一个对象(即它的委托)的引用,并在关键时刻给委托发消息。消息可能只是通知委托发生了某件事情,给委托提供机会执行额外的处理,或者消息可能要求委托提供一些关键的信息以控制所发生的事情。委托的作用主要有两个,一个是传值,一个是传事件。

传值常用在B类要把自己的一个数据或者对象传给A类,让A类去展示或者处理(切分紧耦合,和代码分块时常用)。传事件是A类发生了什么事,把这件事告诉关注自己的类,也就是委托的对象,由委托的对象去考虑发生这个事件后应该做出什么反映(例如在异步请求中,界面事件触发数据层改变等)。利用委托赋值,这种方法是为了不暴露自己的属性就可以给自己赋值,这样方便了类的管理,只有在你想要让别人给你赋值的时候才调用,这样的赋值更可控一些。(如tableView中的委托dateSource等)。

在iOS中委托通过一种@protocol的方式实现,所以又称为协议。协议是多个类共享的一个方法列表,在协议中所列出的方法没有响应的实现,由其它类来实现。delegate是“一对一”的关系,对同一个协议,一个对象只能设置一个代理delegate,所以单例对象就不能用代理。代理更注重过程信息的传输:如发起一个网络请求,是否此时请求已经开始、是否收到了数据、数据是否已经接受完成、数据接收失败等。

从委托类的定义可以看出,委托与协议有一定的相似性。都采用protocol关键字来声明,并且其中的方法都有optional和required,都需要对required方法和调用的optional方法进行实现。而不同的是在委托对象所在的类中需要定义一个delegate对象,并且为id类型。但是delegate与protocol本质上是不同的。Delegate本身应该称为一种设计模式,是把一个类自己需要做的一部分事情,让另一个类(也可以就是自己本身)来完成,而实际做事的类为delegate。而protocol是一种语法,它的主要目标是提供接口给遵守协议的类使用,而这种方式提供了一个很方便的、实现delegate模式的机会。 Block 在Objective-C中被称为Block,在Swift中被成为Closure(在Java中称为Lambda)

Block是Apple Inc.为C、C++以及Objective-C添加的特性,使得这些语言可以用类lambda表达式的语法来创建闭包,是iOS4.0以后和Mac OS X 10.6以后引进的对C语言的扩展,用来实现匿名函数的特性。 Block能够读取其它函数内部变量的函数,在一段请求连续代码中可以看到调用参数(如发送请求)和响应结果。采用Block技术能够抽象出很多共用函数,提高了代码的可读性,可维护性,封装性。

block写法更简练,不需要写protocol、函数等等;block注重结果的传输:比如对于一个事件,只想知道成功或者失败,并不需要知道进行了多少或者额外的一些信息;block需要注意防止循环引用。

1.循环引用问题,如果block里用了self,需要替换为 2.block所在函数中的,捕获自动变量,但是不能修改它,不然就是编译错误。但是可以改变全局变量、静态变量、全局静态变量。为何不让修改变量:这个是编译器决定的。理论上当然可以修改变量了,只不过block捕获的是自动变量的副本,名字一样。为了不给开发者迷惑,干脆不让赋值。静态变量属于类的,不是某一个变量。所以block内部不用调用self指针,所以block可以调用。解决block不能保存值这一问题的另外一个办法是使用__block修饰符。

block和delegate的区别

block不像代理声明了一个代理函数,在调用的类内部还要实现该函数,若一个页面能发送多个请求,并且用多点触控同时触发发送多个请求,那个这个页面的代理函数很难区分是那个请求的结果,只有你的响应消息中带有消息类型可能会分出来,若服务器做的不够强大,当出现异常时,找不发送请求,对于开发来说是个问题。这样多个消息在一个函数里解析也不利于封装。 Block比代理更清晰, Block可以在创建事件的时候区分开来。这也是为什么现在苹果 API 中越来越多地使用 Block而不是 Delegate。

block有三个存储区域_NSConcretStackBlock ,_NSConcretGlobalBlock和_NSConcretMallocBlock。正如它们名字说的那样,说明了block的三种存储方式:栈、全局、堆。如果是定义在函数外面的block是global的,另外如果函数内部的block但是没有捕获任何自动变量,那么它也是全局的。

在栈上block调用copy那么复制到堆上,在全局block调用copy什么也不做,在堆上调用block引用计数增加。 由此我们可以看到delegate运行成本低,block成本很高。block出栈需要将使用的数据从栈内存拷贝到堆内存,当然对象的话就是加计数,使用完或者block置nil后才消除;delegate只是保存了一个对象指针,直接回调,没有额外消耗。相对C的函数指针,只多做了一个查表动作 。

一般来说公共接口,方法也较多用delegate进行解耦 ,iOS有很多例子如最常用tableViewDelegate,textViewDelegate等。异步和简单的回调用block更好 ,iOS有很多例子如常用的网络库AFNetwork等。

大多数 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

void (^matchBlock)(GKMatchEvent eventType, Player player, NSData data, NSError *err); 很明显这种方式既不简便又不易读,所以你可能从未看过这样的解决方案。如果你看过这样的解决方式,但是这显然是一个糟糕至极的代码行,你不会有精力去维护这个。

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

一个对象只能有一个 delegate

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

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

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

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

一般的 delegate 方法会有返回值

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

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

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

过程 vs 结果( Process vs. Results )

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

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

因此我们可以这样认为, delegate 的回调更多的面向过程,而 block 则是面向结果的。如果你需要得到一条多步进程的通知,你应该使用 delegation 。

而当你只是希望得到你请求的信息(或者获取信息时的错误提示),你应该使用block。(如果你结合之前的三个结论,你会发现 delegate 可以在所有事件中维持 state ,而多个独立的 block 确不能)

从上面我们可以得出两个关键点。首先,如果你使用 block 去请求一个可能失败的请求,你应当只使用一个 block 。我们可以看到如下的代码:

[fetcher makeRequest:^(id result) { // do something with result } error:^(NSError *err) { // Do something with error }]; 上面代码的可读性明显比下面 block 的可读性差(作者说这个是他个人不谦虚的观点,译者认为没有那么严重)

文章摘自 推酷 简书

收藏
0
sina weixin mail 回到顶部