3-26.【OC】【Runtime】消息转发的三阶段分别是什么?

6 阅读2分钟

当一个对象无法识别发送给它的消息时,Objective-C Runtime 提供了三道防线来挽救。这三个阶段是由浅入深、性能开销从小到大的过程。如果这三个阶段都走完了仍未处理,程序就会抛出 unrecognized selector sent to instance 异常并崩溃。


第一阶段:动态方法解析 (Dynamic Method Resolution)

这是最先被触发的阶段。系统会询问当前的类:“你能临时为这个方法提供一个实现(IMP)吗?”

  • 核心方法+resolveInstanceMethod: (实例方法) 或 +resolveClassMethod: (类方法)。
  • 做法:开发者可以在此方法内部调用 class_addMethod 动态注入函数实现。
  • 结果:如果返回 YES,Runtime 会重新启动消息寻找流程;如果返回 NO,进入下一阶段。
  • 特点:性能开销最低,因为一旦添加成功,该方法会被缓存,后续调用就像普通方法一样快。

第二阶段:快速转发 (Fast Forwarding)

如果第一阶段没能解决问题,系统会询问对象:“既然你处理不了,有没有其他对象(Target)能处理?”

  • 核心方法-forwardingTargetForSelector:
  • 做法:直接返回一个能够响应这个 SEL 的其他对象。
  • 结果:如果返回一个非 nil 对象,系统会将消息转发给该对象;如果返回 nil,则进入最终阶段。
  • 特点:逻辑简单,只需要转移接力棒。这被称作“快速”是因为它不需要创建复杂的 NSInvocation 对象。

第三阶段:标准转发 (Normal Forwarding)

这是最后也是最灵活的一道防线。系统会把消息的所有细节(方法名、参数、目标)打包成一个 NSInvocation 对象。

  • 核心方法

    1. -methodSignatureForSelector::必须先返回一个正确的方法签名(包含返回值和参数类型)。
    2. -forwardInvocation::拿到打包好的 NSInvocation 后,你可以在这里随心所欲地处理(修改参数、修改返回值、转发给多个对象、或者直接静默处理)。
  • 结果:如果处理了,流程结束;如果未处理,系统执行 doesNotRecognizeSelector:

  • 特点:性能开销最高,但灵活性最强。


总结对比

阶段关键方法核心逻辑性能
1. 动态解析+resolveInstanceMethod:动态添加 IMP极高
2. 快速转发-forwardingTargetForSelector:找个“替死鬼”执行较高
3. 标准转发-forwardInvocation:彻底的逻辑重组较低

实际应用场景建议:

  • 热修复/动态注入:优先选择第一阶段。
  • 多重继承模拟/代理模式:通常选择第二阶段。
  • 防止崩溃/无埋点统计/消息拦截器:通常选择第三阶段,因为它能完全控制整个消息包。