iOS 事件回调机制

首先是对事件回掉的定义简单的介绍, 以及在iOS平台下事件回调的几种方式.

名词定义

事件

由用户操作、具有逻辑的模型、远程的网络响应产生的、由程序员无法控制触发时间的代码执行点。

举例

用户操作:用户单击一个Button

逻辑模型:AVAudioPlayer播放完毕

远程响应:下载进度改变,获得Data

回调

程序员在不知道事件触发点的情况下,写好事件触发后应有的响应和事件处理,并且交给操作系统来监控事件和作出响应处理事件

举例

[UIButton addTarget:action:event]

[AVAudioPlayer.delegate audioPlayer:didPlayCompleted:(BOOL)succeed]

[NSURLSession.delegate URLSession:dataTask:DidReceiveData:]

事件回调的方式

  1. delegate
  2. block
  3. NSNotificationCenter
  4. KVO (Key-Value-Observer)
  5. Target-Action

先定义两个词:

Event Source 事件源

产生事件的对象, 比如:UIButton, AVAudioPlayer, NSURLSession

Event Handler 事件处理者

处理某个事件的对象, 比如:UIViewController.

delegate 代理

说明

事件源: 有个属性delegate, 该delegate实现了一些特定的方法, 为了保证一些方法的存在, 和定义响应方法的方法名, 通常会有一个代理协议.

事件处理者: 实现了代理协议, 并实现了协议中所需要的方法, 方法中写处理事件的代码.
当事件产生, 事件源就会向他的delegate发送消息, 如果消息响应, 就会执行事件处理者(delegate)中的方法, 从而实现事件回调.

特点

一、一个事件处理者通常可以处理多个事件源发出的事件

该事件处理者可以成为多个同类对象的delegate, 并且只需要实现一次delegate方法即可完成多个对象的事件处理.

比如UIViewController实现了UITextFieldDelegate, 则这个vc即可通过一个代理来处理所有的textField对象发出的消息.

优点: 可以减少事件处理实现所需要的方法个数

缺点: 如果不同对象的处理事件的方法不同, 需要协议方法中自己判断事件源.

二、一个事件源通常只有一个delegate, 即只有一个事件处理者

该事件源只能向一个delegate发送事件产生消息. 无法向多个对象发送事件产生消息.

比如UITableView只能有一个dataSource和一个delegate. 重复设置则会覆盖上一次的设置.

优点: 接收事件的对象唯一, 并且拥有该对象的引用, 对于事件源来说是一一对应的, 所以一旦bug出现, 可以立马找到事件处理者(继续调试bug).

缺点: 只能有一个事件处理者. 由于只有一个事件处理者, 所以如果事件源是个单例, 则需要不断修改delegate来改变事件处理者.

[从(一)(二)看出delegate是”多对一"关系]

三、一般delegate中协议方法都有返回值

最常见的就是UITableViewDataSource, 每一个方法都有一个返回值.

优点: 在非事件回调场景中, 可以实现对一个对象的定制.

缺点: 没有(不在同一个层面, 没有缺点可谈)

四、一般一个delegate协议中拥有多个方法, 并且拥有”过程性质"

delegate偏向与一个事件的处理过程, 就是对一个事件的将要产生, 正在发生, 事件结束过程的处理.

如NSURLSessionDelegate, 其拥有接收到Response, 接收到Data, 接收到Error, 下载进度, 下载完毕.

从这些方法即可看出, 一个delegate的方法就是对一个事件的不同时间的处理.

再如UITextFieldDelegate, 有TextField能否编辑, 将要编辑, 已经开始编辑, 内容改变, 能否返回, 将要返回, 已经返回. 也是一系列随着时间变化的处理方法过程.

block 闭包

说明

事件源: 有一个属性block, 存放要处理事件的代码, 该代码虽然是以属性的形式存在与事件源, 但是代码的内容是由事件处理者产生, 并将整段代码传给事件源.

事件处理者: 实现一段代码, 将这段代码以参数(或者属性)的方式传给事件源. 在事件发生后事件源无需再通知事件处理者, 自己执行那段代码.

特点:

一、一个事件处理者可以处理多个事件源发出的事件

该特点表面与delegate一样, 但是底层仍旧有很多差别

  1. 处理事件的代码是多个, 而不是一个
    在block回调方法中, 事件处理者必须为每一个事件源传递处理事件的代码, 每段代码均是相对独立存在的对象, N个事件源就会对应N段事件处理代码, 也就是N个处理对象. 会占用内存.
    在delegate回调方法中, 事件处理者只需要写一段事件处理的代码, 可供多个事件源使用, N个事件源对应1个事件处理代码, 也就是1个delegate.

  2. 事件源无法知道谁是处理者, 只能知道处理者干了什么
    在block回调方法中, 事件源只能得到事件处理者的”处理任务”, 但是无法得到这个任务到底是谁给它的, 也就是说并没有一个引用指向处理者.

在delegate回调方法中, 事件源拥有一个属性delegate, 可以通过delegate得到事件处理者.

优点: 对不同对象的处理定制

缺点: 处理代码块容易占用内存.

二、一个事件源可能动态持有block

该特点与delegate的(二)相反, 一个事件源在不同时间发出类似事件, 可以有不同的处理block.

[PS: 动态持有代表block可以改变, 而不是”一对多”的关系].

下面举一个小新老师教的例子:

在BaseNetwork的子类中, 有不同的”GET”开头的类方法, 每个方法都有block. 每次AFN得到请求后, 都会调用block, 从而实现一个方法满足不同环境的需要, 也就是不同的处理代码.

三、一般block都没有返回值

这个显而易见, 你写的block中并没有return.

四、一般只传一个到两个block给事件源, 并且拥有”结果性质”

这个”结果性质”是相对于delegate的”过程性质”而言.

定义block时, 通常用completed, completedHandle, failed, error等字眼, 表示这个事件结果是成功, 还是失败.

不难看出, block只是在一个事件结束后, 对这个事件的结果处理. 与delegate比较, 没有过程的性质存在.

比如AFN中, GET请求方法离, 只需要传递成功后的block, 和失败后的block, 不需要考虑在请求过程得到的Response和Data.

NSNotificationCenter 通知中心

说明

事件源: 通过系统共用的通知中心发送一个通知

事件处理者: 向系统共用的通知中心注册需要接收的通知名称, 在通知被事件源发出后, 系统会自动发送给所有注册该通知的事件处理者.

事件源不需要关心事件处理者的个数, 只需要发送事件发生的通知即可. 事件处理者也不需要关心谁发出来的事件, 只需要处理事件即可.

特点

一、一个通知可以由任何对象发出

一般只由一个对象发出.

二、一个通知可以有很多个接收者

自己写的通知接收者一般只有一个或多个, 系统发出的通知的接收者一般有多个.

从(一)(二)点看出, 事件源和事件处理者是”一对多"的关系.

三、方便

事件源和事件处理者的跨度很大

事件源不需要得到事件处理者的引用, 事件处理者也不需要得到事件源的引用.

优点: 随时随地都可以使用

缺点: 如果很多通知, 很多接受者, 很多发送者, 然后出现了bug, 然后就没有然后了...

KVO (Key-Value-Observer) 观察者

说明

事件源: 自己的某个属性被修改了, 则告诉监控自己的Observers.

事件处理者: 添加一个针对事件源的某个属性的Observer, 监控该事件源的属性.

事件源任何时间改变自己的一个Key对应的Value(也就是属性的改变), 就会通知Observer. Observer接收到消息后便会调用设定的方法.

特点、

一、同上, 也是”一对多”的关系

二、观察者模式非常占用系统资源

优点: 很简单的实现KV监控, 无需开线程和Timer.
缺点: 占用大量的系统资源

慎用.

Target-Action 目标行动

说明

事件源: 拥有Target-Action列表,

事件处理者: 向事件源的Target-Action列表注册事件, 以及target(self)的一个action(SEL).

事件源发生事件后, 向Target-Action列表发送事件消息.

特点

一、支持“多对多”, 这种情况很少用

二、UIControl中大量使用

系统自带的UIControl均使用这种模式回调

三、在非UIControl中自己实现Target-Action

类似delegate, 但是与delegate最大的区别就是不需要协议来限制方法名.

用这种方法的话, 回调时候传参中, 除了第一参数是self(事件源引用), 最好不要有第二个参数. 因为事件源只能知道参数个数, 不能知道参数的类型.

还要注意的是Target-Action的数组, 实现这个数组很麻烦.

所以自己定义的话最好是setTarget:Action:而不是addTarget:Action:, 这样的结果就是”多对多”变成”一对一”.

那么, 问题来了...

这么多的回调方式, 在实际开发中如何选择?

首先必须要明确的一点: 如果一个事件的事件源和事件处理者是”一对一”的关系, 以上5个回调方式全部都可以实现.

既然都可以实现, 那就可以随便选择了?

理论如此, 但是最好酌情选择, 具体情况具体分析.

从优先级排序的话, 可以这么排

  1. block 处理事件结果, 代码简洁美观, 基本的回调功能都有, 满足各种情况的回调需求.
  2. delegate 如果出现需要返回值, 有过程性质的存在, 可以代替block.

以下回调方式在上面无法实现的情况下去使用

  1. NSNotificationCenter 1和2都不能实现一对多, 在这种情况下去使用.
  2. Target-Action 自己很少实现, 一般使用系统自带的.
  3. KVO 慎用.

你可能感兴趣的:(iOS 事件回调机制)