3-25.【OC】【Runtime】动态方法解析成功后,是否还会进入消息转发流程?

0 阅读2分钟

不会。

如果“动态方法解析”成功(即 resolveInstanceMethod:resolveClassMethod: 返回了 YES),Runtime 会认为该方法现在已经存在,并重新尝试查找并执行该方法。由于你已经通过 class_addMethod 注入了实现,这次查找会直接命中,从而正常执行函数,流程到此结束。

只有当动态解析返回 NO,或者返回了 YES 但你并没有真正添加对应的 IMP 时,系统才会进入后续的**消息转发(Message Forwarding)**流程。


1. 消息处理的“全景流水线”

为了让你看清动态解析的位置,我们可以把整个过程想象成一个三关的挑战。一旦某一关通过,后面的关卡就会被直接跳过。

  1. 第一关:动态方法解析 (Dynamic Method Resolution)

    • 询问类:“你能临时变出这个方法吗?”
    • 如果 YES \rightarrow 重新执行方法,流程结束
  2. 第二关:快速转发 (Fast Forwarding)

    • 询问对象:“既然你不会,有人能替你做吗?”
    • 如果返回了非 nil 的转发目标 \rightarrow 由新目标执行,流程结束
  3. 第三关:标准转发 (Normal Forwarding)

    • 询问对象:“请把这整件事打包,我看看怎么处理。”
    • 如果处理了 NSInvocation \rightarrow 流程结束;否则,doesNotRecognizeSelector: 抛出崩溃。

2. 为什么解析成功后要“重新查找”?

这是底层实现的一个精妙之处。当你返回 YES 时,Runtime 的底层汇编代码会重置查找指针,再次通过 objc_msgSend 的逻辑去搜一遍。

  • 性能考虑:因为你刚刚用 class_addMethod 把方法塞进了类的方法列表里,下次调用这个方法时,它就会像普通方法一样被缓存到 cache_t
  • 无感执行:重新查找确保了动态添加的方法能够像原生方法一样,拥有完整的缓存加速和执行链路,而不需要每次都走昂贵的“转发”逻辑。

3. 注意一个常见的“坑”

如果你在 resolveInstanceMethod: 里返回了 YES,但是忘记调用 class_addMethod

  1. Runtime 收到 YES,于是满怀信心去重新查找。
  2. 结果发现还是找不到 IMP
  3. 这时,系统会意识到你“骗”了它,流程会再次进入动态解析(有些版本会直接跳过)或者直接进入消息转发。
  4. 如果循环处理不好,甚至可能导致死循环或直接崩溃。

4. 总结对比:动态解析 vs 消息转发

特性动态方法解析消息转发 (Fast/Normal)
触发时机第一时间(找不到就触发)动态解析失败后触发
性能开销(后续调用会进入缓存)(每次都需要创建转发对象或 Invocation)
核心目的为当前类添加新能力将压力转嫁给其他对象或处理逻辑
代码位置作用于“类”级别 (+ 方法)作用于“实例”级别 (- 方法)