理解消息转发机制

  消息转发分为两大阶段。第一阶段先征询接收者,所属的类,看其是否能动态添加方法,以处理当前这个“未知的选择子”(unknown selector),这叫做“动态方法解析”(dynamic method resolution)。第二阶段涉及“完整的消息转发机制”。如果运行期系统已经把第一阶段执行完了,那么接收者自己就无法再以动态新增方法的手段来响应包含该选择子的消息了。此时,运行期系统会请求接收者以其他手段来处理与消息相关的方法调用。这又细分为两小步。首先,请接收者看看有没有其他对象来处理这条消息。若有,则运行期系统会把消息转发给那个对象,于是消息转发过程结束,一切如常。若没有“备援的接收者”,则启动完整的消息转发机制,运行期系统会把与消息有关的全部细节都封装在NSInvocation对象中,再给接收者最后一次机会,令其设法解决当前还未处理的这条消息。

  注意:选择子就是方法,就是@selector(methodName)这个玩意,也叫方法选择器。

  一,动态方法解析

  对象在收到无法解读的消息后,首先将调用其所属类的下列类方法:

+  (BOOL)resolveInstanceMethod:(SEL)selector

  该方法的参数就是那个未知的选择子,其返回值为Boolean类型,表示这个类是否能新增一个实例方法用以处理此选择子。

  如果是类方法,则会调用:

+ (BOOL)resolveClassMethod:(SEL)selector

使用这种方法的前提是:相关方法的实现代码已经写好,只等运行的时候动态插在类里面就可以了。

  二,完整的消息转发

  如果运行期系统已经执行完了动态方法解析,消息还没有被处理,那么消息接受者自己就无法再以动态新增方法的形式来响应包含该未知选择子的消息了,此时就进入了第二阶段——完整的消息转发。运行期系统会请求消息接受者以其他手段来处理与消息相关的方法调用。

  1 备援接收者

  当前接收者还有第二次机会能处理未知的选择子,在这一步中运行期系统会问它:能不能把这条消息转给其他接收者来处理。就会调用如下方法:

- (id)forwardingTargetForSelector:(SEL)selector

这里的返回值,就是备援接收者,它会继续处理这个消息。

在一个对象内部,可能还有一系列其他对象,该对象可经由此方法将能够处理某选择子的相关内部对象返回,这样的话,在外界看来,好像是该对象亲自处理了这些消息似的。

  2 完整的消息转发

  如果消息还没有被处理,转发算法就会来到这一步。首先创建NSInvocation对象,把尚未处理的那条消息有关的全部细节都封装其中。“消息派发系统”将把消息指派给目标对象。这里的目标对象可以自定义。此步骤会调用下列方法:

 (void)forwardInvocation:(NSInvocation *)invocation

实现此方法时,如果发现调用操作不应该由本类处理,则需要沿着继承体系,调用父类的同名方法,这样一来,继承体系中的每个类都有机会处理这个调用请求,直至rootClass,也就是NSObject类。如果最后调用了NSObject的类方法,那么该方法还会继而调用”doesNotRecognizeSelector:“以抛出异常,此异常表明选择子最终也未能得到处理。消息转发到此结束。

关于does NotRecognizeSelector:你可能感到陌生,但是对于类似于unrecognized selector send to instance xxx这样的错误,你可能并不陌生。这种错误通常是因为调用了某个对象或者某个类里不存在的方法,从而触发了消息转发机制,最终把这个未识别的消息发送给了NSObject的默认实现。

三,消息转发全流程:

摘录自《Effetive Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》第12条:理解消息转发机制

DEMO1:https://github.com/caigee/iosdev_sample下的ClassForward

DEMO2:https://github.com/huanglonghui/MessageForwardingTest

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java架构

深入理解Java虚拟机到底是什么什么是Java虚拟机从进程的角度解释JVM

1805
来自专栏开发与安全

套接字socket 的地址族和类型、工作原理、创建过程

注:本分类下文章大多整理自《深入分析linux内核源代码》一书,另有参考其他一些资料如《linux内核完全剖析》、《linux c 编程一站式学习》等,只是为...

27311
来自专栏XAI

【Python3-API】情感倾向分析示例代码

Python3-urllib3-API情感倾向分析示例代码 AccessToken获取可以参考:http://ai.baidu.com/forum/topic/...

3339
来自专栏Crossin的编程教室

【Python 第38课】 模块

如果说我比别人看得更远些,那是因为我站在了巨人的肩上。 -- 牛顿 python自带了功能丰富的标准库,另外还有数量庞大的各种第三方库。使用这些“巨人的”代码...

28510
来自专栏JAVA高级架构

深入理解Java虚拟机到底是什么

什么是Java虚拟机 作为一个Java程序员,我们每天都在写Java代码,我们写的代码都是在一个叫做Java虚拟机的东西上执行的。但是如果要问什么是虚拟机,恐怕...

3077
来自专栏求索之路

从零开始仿写一个抖音App——Apt代码生成技术、gradle插件开发与protocol协议

本文首发于简书——何时夕,搬运转载请注明出处,否则将追究版权责任。交流qq群:859640274

1994
来自专栏王大锤

理解消息转发机制

2033
来自专栏IT技术精选文摘

从Java视角理解系统结构(三)伪共享

从我的前一篇博文中, 我们知道了CPU缓存及缓存行的概念, 同时用一个例子说明了编写单线程Java代码时应该注意的问题. 下面我们讨论更为复杂, 而且更符合现实...

2087
来自专栏Android开发与分享

Android开发架构规范前言命名规范编程规范代码提交规范架构规范参考文章

3918
来自专栏Java后端技术栈

Java虚拟机OOM之虚拟机栈和本地方法栈溢出(4)

(1)如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError 异常; (2)如果虚拟机栈可以动态扩展(当前大部分的 Java ...

843

扫码关注云+社区

领取腾讯云代金券