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

推荐阅读更多精彩内容

  • iOS网络架构讨论梳理整理中。。。 其实如果没有APIManager这一层是没法使用delegate的,毕竟多个单...
    yhtang阅读 5,094评论 1 23
  • 基础 1. 为什么说Objective-C是一门动态的语言? 2. 讲一下MVC和MVVM,MVP? 3. 为...
    波妞和酱豆子阅读 3,235评论 0 47
  • 他叫盼盼,比我大两岁,老家住在河南荥阳的一个小村庄里,家里的父母弟弟都是靠种地维持生计,一年前他来到上海打拼,而我...
    etuan5252阅读 300评论 0 0
  • 他来了,又走。 他说了,也做。 一遍一遍。 直到厌倦。 如烟消散。 像昨天, 又像永远。
    H3190阅读 103评论 0 1
  • 本剧为建国七十年而创作,计划于十月初开拍。 【时间背景】1936年秋,大约“西安事变”发生前的两个月…… 【人物】...
    邓文伟阅读 373评论 0 3