13.动态决议与消息转发
前言
在上一文中我们有探究到objc_msgSend相关的一些底层原理。
我们发现在慢速查找的过程中有这样
forward_imp 消息转发
resolveMethod_locked 动态决议
本文就将对这两个地方做深入探究
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
IMP imp = nil;
Class curClass;
...
for (unsigned attempts = unreasonableClassCount();;) {
if (curClass->cache.isConstantOptimizedCache(/* strict */true)) {
...
} else {
...
if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = forward_imp;
break;
}
}
...
}
// No implementation found. Try method resolver once.
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
...
done_unlock:
runtimeLock.unlock();
if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
return nil;
}
return imp;
}先附上一张流程图
仅从源码分析会很困难,结合代码调试各种情况,和这张流程图可以更好的理解整个流程的细节
我自己也反复调试了很多次才理的差不多,以前知道这些东西,但没有深入底层去理解细节
这张图也改了好几次

一、 _objc_msgForward_impcache
通过在源码objc-msg-arm64.s中搜索我们一步步找到核心代码
__objc_msgForward_impcache
__objc_msgForward
__objc_forward_handler
我们并没有找到
__objc_forward_handler怀疑不是汇编代码,去掉
_试试
objc_defaultForwardHandler
找到了下面的代码。
二、 resolveMethod_locked
三、实例方法动态决议
这里声明一个实例方法,但不做实现。触发调用
会见到我们常见的一个崩溃信息:
3.1 动态添加Method
重写 + (BOOL)resolveInstanceMethod:(SEL)sel 添加method
输出:
3.2 如果再次调用,还会不会进行动态决议呢?
理论分析:第一次添加了Method后,会同步插入到方法缓存中,所以第二次执行就不会再次添加。
实际运行结果也验证了刚才的分析。
3.3 源码解析
四、类方法动态决议
4.1 动态添加Method
如果你写成上面的样子,你会发现依旧会崩溃。根本没生效。
为什么呢?
如果你不知道为什么,你需要首先搞明白
类方法存在哪里深入探索Class的结构这篇文章可以带你了解
正确的实现方式:
输出:
4.2 类方法动态决议的特殊处理
在源码中及流程图中可以发现,在 resolveClassMethod 之后如果缓存中还没有对应的Method的话就会再去调用 resolveInstanceMethod。
可见苹果工程师也是操碎了心啊,给了好多机会来让我们不要崩溃
五、消息转发
在 lookUpImpOrForward 中我们找到 _objc_msgForward_impcache
5.1 寻找切入点
发现其实现在 objc-msg-arm64.s 中
__objc_msgForward_impcache
__objc_msgForward
_objc_forward_handler
我们有进一步找到了
_objc_forward_handler拥有一个默认的实现:
objc_defaultForwardHandler以及一个
set方法:objc_setForwardHandler
进一步搜索 setForwardHandler 并未发现相关调用,推测调用处不在OC源码中。

分析Log
发现调用了 CoreFoundation 中的 ___forwarding___ 方法。
那么怎么去进一步分析呢?
5.2 汇编分析 CoreFoundation`forwarding:
forwardingTargetForSelector:
0x7fff203f741b <+184>: mov r14, qword ptr [rip + 0x604ed906] ; "forwardingTargetForSelector:"
通过这条指令我们发现一个 forwardingTargetForSelector:
我们找下源码,根据命名推测这个方法用来是转发消息给某个对象的。
我们下个断点:
发现断点确实走到了这里,输出堆栈信息:
接下来我们试着在这里返回一个对象来接收消息看看效果。
运行后执行正常,输出了 -[RYSubModel noIMP]。
5.3 转发对象为空的情况
当不实现 forwardingTargetForSelector 的时候,我们发现,经过一次动态决议默认返回 nil 后,又一次进入了 动态决议
behavior == 2 == LOOKUP_RESOLVER满足动态决议的条件,再次进行
动态决议behavior ^= LOOKUP_RESOLVERbehavior变成0了
六、总结
这部分的理解还是需要结合调试,单纯看源码很难理清楚,有太多嵌套调用。
希望我的流程图能帮助理解,有不对的地方也欢迎指出。
Last updated
Was this helpful?