iOS 消息转发机制
这篇博客的前置知识点是 OC 的消息传递机制,如果你对此还不了解,请先学习之,再来看这篇。这篇博客我尝试用口语的方式像讲述 PPT 一样给大家讲述这个知识点。
我们来思考一个问题,如果对象在收到无法解读的消息时,会发生什么?例如,我们实现一个 viewcontroller,其中并没有一个成员方法名为『setText:』,当编写这条语句时
[selfsetText:@"你好"];

示例
由于 OC 是一门动态语言,在编译期只是显示一条 warning,而不是阻止运行的 error。如果忽略 warning 运行,程序会 crash,在控制台会显示类似
unrecognized selector sent to instance0x7f931a4180d0
的报错信息。

unrecognized selector
消息被发送给了不能处理它的对象。我们学习 iOS 的消息转发机制可不是为了故意造这样的 crash 玩,说上面的这个例子,是为了说明如果我们不通过消息转发机制做任何事情的话,系统最终会以 crash 结束。等等,刚才我们说到 OC 是一门动态语言,那么是否可以在运行期做一些事来让 crash 不会发生呢?
消息转发机制就是来干这件事的,在运行期通过3个『接盘侠』方法,给对象和消息更多的机会来完成成功的调用,而不是直接 crash。
一号接盘侠
第一个接盘侠代表动态方法解析阶段,对应的具体方法是+(BOOL)resolveInstanceMethod:(SEL)sel 和+(BOOL)resolveClassMethod:(SEL)sel,当方法是实例方法时调用前者,当方法为类方法时,调用后者。这个方法设计的目的是为了给类利用 class_addMethod 添加方法的机会。
看下面这个示例,MyTestObject类重写了第一个接盘侠方法,可以看到这个方法传入一个 selector,返回 BOOL 类型。被传入的 selector 就是未被处理的方法,在一号接盘侠方法中,判断若方法名为 XXX 则给这个类添加同名的方法,把方法的实现指向跟 XXX 名字不一致的 AAA,并返回 YES。若 selector 名字不是 XXX,就返回父类。

resolveInstanceMethod
通过这个示例,可以看出,我们可以通过一号接盘侠方法让 方法名和方法实现在运行期任意搭配。
再说一下这个返回值,其实可以试验一下,无论返回 YES 还是 NO,系统都会尝试用 SEL 来寻找 IMP,如果找到函数实现,则执行,所以无论返回 YES\NO都会进入二号接盘侠方法。
二号接盘侠
第二个阶段是备援接收者阶段,对象的具体方法是-(id)forwardingTargetForSelector:(SEL)aSelector ,此时,运行时询问能否把消息转给其他接收者处理,也就是此时系统给了个将这个 SEL 转给其他对象的机会。我们继续来研究下参数和返回值,参数和一号接盘侠一样,都是 selector,返回值是 id 类型,当返回 非self\非nil 时,消息被转给新对象执行。

forwardingTargetForSelector
三号接盘侠
第三个阶段是完整消息转发阶段,对应方法-(void)forwardInvocation:(NSInvocation *)anInvocation,这是消息转发流程的最后一个环节。参数 anInvocation 中包含未处理消息的各种信息(selector\target\参数...)。在这个方法中,可以把 anInvocation 转发给多个对象,与二号接盘侠不同,二号只能转给一个对象。

forwardInvocation
如果上述3个方法都没有来处理这个消息,就会进入 NSObject 的-(void)doesNotRecognizeSelector:(SEL)aSelector方法中,抛出异常。等等,为什么我们不能通过给 NSObject 创建一个 category,重写这个方法,在这里处理消息未被处理的情况呀?在苹果的官方文档中,明确提到,“一定不能让这个函数就这么结束掉,必须抛出异常”。除了听官方文档的话,其实在分类中通过重写该方法处理各种消息未被处理的情况,会让这个分类的方法特别长,不利于维护。而且还有个原因,明明方法名叫『无法识别 selector』,其中却是一大堆处理该情况的代码,也很奇怪。

doesNotRecognizeSelector
总结
总结一下整个消息转发的流程:

消息转发的流程
可以通过重写3个接盘侠方法,在其中打断点来验证执行顺序。

断点验证顺序
总结:
在一个函数找不到时,OC提供了三种方式去补救:
1、调用resolveInstanceMethod给个机会让类添加这个实现这个函数
2、调用forwardingTargetForSelector让别的对象去执行这个函数
3、调用forwardInvocation(函数执行器)灵活的将目标函数以其他形式执行。
如果都不中,调用doesNotRecognizeSelector抛出异常。
疑问
Q1:那我们只用最后一个接盘侠方法多好啊,为什么还需要前2个呢?
其实还与这3个方法的用途不同有关:
运行期添加方法,用1;
转发给另1个对象、改变方法时,用2;
需要转发给多个对象时,用3;
而且,步骤越往后,处理消息的代价越大,到最后一个阶段时,都创建了 NSInvocation 对象了。
Q2:消息转发有哪些应用场景呢?
可以在运行期再加入某方法,例如 Teacher 类里有teach方法,DrugDealer 类里有letsCook方法,通过一号接盘侠方法,我们可以在运行期把 saleDrug 偷摸加到 teacher 的方法列表中,让 teacher 具备贩毒的功能,[teacher guessWhatHeDo],实际调用的是[teacher letsCook],唉呀妈呀,绝命毒师啊。
把方法转给其他对象处理,再举个例子,还是 Teacher 类(博主跟老师有仇吗...),[teacher letsCook],可以把对象在运行期换为drugDealer。再来一个 Cook 类,也有 letsCook 方法,但这次这方法不是 cook 毒品,而是 cook 菜。因此既可以通过[teacher letsCook] 实现[drugDealer letsCook],也可以实现[cook letsCook]。相当于 OC 实现了多重继承,虽然有点不太恰当...
注意
respondsToSelector我们再熟悉不过了,用来检查某对象是否实现了某方法。此函数通常是不需要重载的,但是在动态实现了查找过程后,需要重载此函数让对外接口查找动态实现函数的时候返回YES,保证对外接口的行为统一。

respondsToSelector
最后说一下 warning 的事。编译器很好心的报的那个 warning 咋办呢,不管那个小黄条不是一个爱整洁的程序员的风格,所以我们要想办法把它去掉。
有两种方法,第一种比较暴力,通过在配置文件中把 Complier Flag 加-w,对该类去除所有 warning。

去掉所有warning
第二种是推荐的做法,在 xcode 的 error 面板对 warning 右键-Reveal in Log,这里有个小 bug,如果这个选项不可选择,需要你重新 build 一下就可选了,

小 Bug
在右侧,可以看到这个warning 的名称,

如何看warning名称
所以用这个宏把出现 warning 的代码包围起来,就可以让编译器不再报错:
#pragmaclang diagnostic push#pragmaclang diagnostic ignored"-Wobjc-method-access"[self setText:@"你好"];#pragmaclang diagnostic pop
iOS 消息转发机制的更多相关文章
- iOS消息转发机制
iOS消息转发机制 “消息派发系统”(message-dispatch system) 若想令类能够理解某条消息,我们必须实现出对应的方法才行.但是,在编译器向类发送其无法解读的消息时并不会报错,因为 ...
- iOS的消息转发机制详解
iOS开发过程中,有一类的错误会经常遇到,就是找不到所调用的方法,当然这类问题比较好解决,给当前对象或其父类对象添加该方法即可,使得编译器在编译时能正确找到该方法:或者,还有另外的方法,由于Objec ...
- iOS Runtime的消息转发机制
前面我们已经讲解Runtime的基本概念和基本使用,如果大家对Runtime机制不是很了解,可以先看一下以前的博客,会对理解这篇博客有所帮助!!! Runtime基本概念:https://www.cn ...
- iOS runtime探究(二): 从runtime開始深入理解OC消息转发机制
你要知道的runtime都在这里 转载请注明出处 http://blog.csdn.net/u014205968/article/details/67639289 本文主要解说runtime相关知识, ...
- iOS消息转发
消息转发是一种功能强大的技术,可以大大增加Objective-C的表现力.什么是消息转发?简而言之,它允许未知的消息被困住并作出反应.换句话说,无论何时发送未知消息,它都会以一个很好的包发送到您的 ...
- Effective Objective-C 2.0 — 第12条:理解消息转发机制
11 条讲解了对象的消息传递机制 12条讲解对象在收到无法解读的消息之后会发生什么,就会启动“消息转发”(message forwarding)机制, 若对象无法响应某个选择子,则进入消息转发流程. ...
- 理解Objective-C Runtime(三)消息转发机制
消息转发机制概述 上一篇博客消息传递机制中讲解了Objective-C中对象的「消息传递机制」.本文需要讲解另外一个重要问题:当对象受到无法处理的消息之后会发生什么情况? 显然,若想令类能理解某条消息 ...
- runtime消息转发机制
Objective-C 扩展了 C 语言,并加入了面向对象特性和 Smalltalk 式的消息传递机制.而这个扩展的核心是一个用 C 和 编译语言 写的 Runtime 库.它是 Objective- ...
- iOS 消息转发以及 NSProxy 实战
最后更新: 2018-01-17 一.消息派发机制-NSObject 在 iOS 开发中, 调用对象的方法就是给对象发送一个消息.了解消息的派发机制对于iOS开发来说是一个很实用且强大的工具, 下面我 ...
随机推荐
- 百度地图 获取两点坐标之间的驾车距离(非直线距离) c#
百度接口了解: http://lbsyun.baidu.com/index.php?title=webapi/route-matrix-api-v2 起点与终点为多对多关系,如果你只想取两个坐标,那就 ...
- centos6.5新增加硬盘挂载并实现开机时自动挂载
在内网主机新增一个2T硬盘,先关机断电再连接硬盘数据线和电源线! 查看当前磁盘设备信息: [root@tb ~]# fdisk -lWARNING: GPT (GUID Partition Table ...
- Chrome F12学习
Chrome实用调试技巧 Chrome调试工具常用功能整理 Google Chrome开发者工具使用(图文教程) 如何更专业的使用Chrome开发者工具
- apache2.2 做后端,增加真实ip到日志中
apache2.2使用mod_remoteip模块 一.安装 wget https://github.com/ttkzw/mod_remoteip-httpd22/raw/master/mod_rem ...
- Git和Github简单教程(转)
这篇文章只挑一部分命令来讲,差不多够用就行的程度.如果需要其他命令,到时候再去其他地方了解就行了先在Github上写好再搬过来的:本文Github链接 目录: 零.Git是什么 一.Git的主要功能: ...
- Source Insight 3.X 插件新loader发布
[前言] 大约一年多没更新SI插件了,主要原因是我从SI换到了sublime,使用SI少了,插件也就停止更新了.不过看到园子里这么多网友的留言,觉得还是有必要更新一下,算是给还在用SI的朋友们一个交代 ...
- Linux学习笔记
性能问题排查: Linux系统出现了性能问题,一般我们可以通过top.iostat.free.vmstat等命令来查看初步定位问题.内存资源占用:free命令 IO占用:iostat -d -k 1 ...
- 《javascript高级程序设计》第三章学习笔记
Undefined类型 该类型只有一个值,即undefined. 对未初始化的变量和未定义的变量,用typeof检测,都会返回'undefined' Null类型 该类型只有一个值,null.并且从逻 ...
- Oracle 11g中修改默认密码过期天数和锁定次数
Oracle 11g中修改默认密码过期天数和锁定次数 密码过期的原因一般有两种可能: 一.由于Oracle中默认在default概要文件中设置了"PASSWORD_LIFE_TIME=180 ...
- vmware centos nat模式下连不上网络解决办法
简单来讲,当你创建一台虚拟机时,VMware为你虚拟了三种接入网络的方式:桥连接,NAT,使用主机网络,Vmware 10中默认对应 VMnet0,VMnet1,VMnet8 . 当选择桥连接方 ...