delegate

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

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

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

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

委托模式的实现思路:

1、通常是在对象主体包含一个委托对象的弱引用:

@property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate;

2、委托对象的实现有两种方式:一种是必须实现,一种是可选实现,即@required和@optional的区别。

@protocol UITableViewDelegate<NSObject, UIScrollViewDelegate>

@optional
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;

3、判断触发委托方法。

//确定委托是否存在Entered方法
if([delegate respondsToSelector:@selector(method:)])
{
//发送委托方法,方法的参数为用户的输入
[delegate method:xxx];
}

4、连接对象主体和委托,通过setDelegate:(id)obj来实现。

_test.delegate = self;

5、如果实现委托的类有委托需要的方法就执行方法。大概就是这样。

Block

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

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

block的写法大概就是这样:

void (^blockTest)(void) = ^{
NSLog(@"block");
}; blockTest();

如果用block进行两个类之间的互动,需要这样:
BLOCK首先你在.h文件中声明BLOCK对象,当然返回的参数你可以自己定义:

typedef void (^blockTest)(); 

@property (nonatomic,copy) blockTest myblock;

在.m文件中执行时:

if(_myblock){
_myblock();
}

其他类执行block时是这样的:

_testClass.myblock = ^{
NSLog(@"block");
}

需要注意的是:
1.循环引用问题,如果block里用了self,需要替换为

__weak typeof(self) weakSelf = 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但是没有捕获任何自动变量,那么它也是全局的。

typedef  int  (^blk_test)( int );

for(...){

blk_test blk = ^(int count) {return count;};
}

虽然,这个block在循环内,但是blk的地址总是不变的。说明这个block在全局段。在栈上block调用copy那么复制到堆上,在全局block调用copy什么也不做,在堆上调用block引用计数增加。

测试方法如下:

typedef  int  (^blkt1)(void) ;

-(void) stackOrHeap{

__block int val =10;

int *valPtr = &val;//使用int的指针,来检测block到底在栈上,还是堆上

blkt1 s= ^{

    NSLog(@"val_block = %d",++val);

    return val;};

s();

NSLog(@"valPointer = %d",*valPtr);

}

int val肯定是在栈上的,我保存了val的地址,看看block调用前后是否变化。输出一致说明是栈上,不一致说明是堆上。有兴趣的可以手动试一下ARC和MRC下的结果。

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

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

 
文/吴白(简书作者)
原文链接:http://www.jianshu.com/p/bbccac805641
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

代理委托和block的更多相关文章

  1. OS笔记047代理传值和block传值

    在两个不同的控制器之间传递数据,可以使用代理传值或者block传值. 例子是一个简单通讯录. 主界面如下: 添加联系人界面 查看/编辑联系人界面:默认是查看模式,点击编辑后进入编辑模式 编辑模式 数据 ...

  2. iOS 再谈 代理传值,block反向传值

    本贴的例子是:有A和B两个界面,要实现的效果就是先让A跳转到B,然后B中有个颜色的参数,当B跳转到A时,把这个颜色的参数传递给A,在A中利用这个颜色改变自己界面的颜色. 第1步:在发送者(界面B)中, ...

  3. 【C#】事件(Event)和代理/委托(Delegate)

    代理(Delegate)的例子 delegate void MyDelegate(string str,int index); // 声明代理 class Test { public static v ...

  4. JavaScript中的事件代理/委托

    事件委托在JS高级程序设计中的定义为"利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件" 如何理解上面的这句话呢,在网上,大牛们一般都使用收快递这个例子来解释的, ...

  5. UEC++ 代理/委托

    代理: 代理可以帮助我们解决一对一或是一对多的任务分配工作.主要可以帮助我们解决通知问题.我们可以通过代理完成调用某一个对象的一个函数,而不直接持有该对象的任何指针. 代理就是为你跑腿送信的,你可以不 ...

  6. C# 代理/委托 Delegate

    本文转载自努力,努力,努力 1. 委托的定义:委托是函数的封装,它代表一"类"函数.他们都符合一定的签名:拥有相同的参数列表,返回值类型.同时,委托也可以看成是对函数的抽象,是函数 ...

  7. iOS delegate, 代理/委托与协议.

    之前知知道iOS协议怎么写, 以为真的跟特么java接口一样, 后来发现完全不是. 首先, 说说应用场景, 就是当你要用一个程序类, 或者说逻辑类, 去控制一个storyboard里面的label, ...

  8. UEC 利用代理/委托写一个生命组件

    首先基于ActorComponent创建一个组件 HealthComponent,将需要的变量与函数创建 #include "CoreMinimal.h" #include &qu ...

  9. js中事件代理(委托)

    var oul = document.getElementById(‘uli’); oul.onclick = function(e) { e = e || window.event; var tar ...

随机推荐

  1. MS SQLSERVER中如何快速获取表的记录总数

    在数据库应用的设计中,我们往往会需要获取某些表的记录总数,用于判断表的记录总数是否过大,是否需要备份数据等.我们通常的做法是:select count(*) as c from tableA .然而对 ...

  2. Unity3d获取APK签名及公钥的方法

    在Unity3d项目中获取APK包签名公钥的方法,核心思想就是通过JNI调用Android提供的方法.不过Unity3d提供了比JNI更上一层的类AndroidJavaObject以及继承它的Andr ...

  3. The last packet successfully received from the server was 2,926,157 milliseconds ago. The last packet sent successfully to the server was 2,926,158 milliseconds ago. is longer than the server configured value of 'wait_timeout'. 解决办法

    Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully rec ...

  4. Go语言实现简单的一个静态WEB服务器

    package main import ( "net/http" ) func main() { http.Handle("/", http.FileServe ...

  5. .Net Log4Net配置多文件日志记录

    其他配置详情在网上都可以找到,但是很多看着都晕,本人就记录一下如何使用: 1.按不同级别(官方说明)可记录的日志级别有: Info.Warn.Error.Debug 2.可以按着四个配置四个输出日志路 ...

  6. adb shell

    1.获取进程ID adb shell ps |findstr packagename 2.获取cpu的值 adb shell dumpsys cpuinfo |findstr packagename ...

  7. JS 4 新特性:混合属性(mixins)之二

    Mixins many classes[混合许多个类] 迄今为止,我们已经学会了简单的继承,我们还能够通过使用mixins处理机制来混合许多类.源于这种理念是非常简单的:我们能够把许多个类最终混合到一 ...

  8. 关于 DWZ 弹出框

    1.弹出框 <a name="***" class="***" href="${** }/***.do?action=***&属性=${ ...

  9. JDBC代码示例

    package test; import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;imp ...

  10. 第十章 嵌入式Linux的调试技术

    对调试工具进行简介.Linux中提供了一类工具,通过这些工具可以逐行跟踪程序的代码,用于测试用户空间程序的gdb.gdbserver和调试内核空间程序的kgdb. 用gdb调试用户空间程序:gdb可跟 ...