1-13.【OC】【基础】Objective-C 是如何支持继承链方法查找的?

1 阅读2分钟

Objective-C 的继承链方法查找,本质上是围绕 objc_msgSend 函数展开的一场在 objc_class 结构体 之间的递归搜索。

由于 Objective-C 的动态性,它不会在编译期就固定方法的调用地址,而是通过以下步骤在运行时沿继承链向上回溯:


1. 查找的起点:isa 指针

当你执行 [student walk] 时,Runtime 首先查看 student 实例内部的 isa 指针

  • 如果是实例方法-),isa 指向 Student 类对象。
  • 如果是类方法+),isa 指向 Student 元类对象。

2. 快速路径:Cache 命中(第一优先级)

在进入繁琐的继承链搜索之前,Runtime 会先检查当前类对象的 cache (方法缓存)

  • 每个类都有一个哈希表记录最近调用的方法。
  • 如果 walk 方法就在缓存里,直接提取 IMP(函数指针)并跳转执行。这极大地抵消了动态查找带来的性能损耗。

3. 慢速路径:本类方法列表(Method List)

如果缓存没中,Runtime 会进入当前类的 class_rw_t 结构体,遍历其 methods (方法列表)

  • 编译器将 @implementation 里的代码编译成了带 SEL(名字)和 IMP(地址)的列表。
  • 找到则存入缓存并执行;找不到,则开始继承链回溯

4. 继承链回溯:superclass 指针

这是支持继承的核心动作。如果当前类找不到方法:

  1. Runtime 提取当前类对象的 superclass 指针
  2. 跳转到父类(比如 Person 类)中。
  3. 重复上述步骤:先看父类的 cache,再看父类的 methods
  4. 以此类推,一直向上追溯到 根类 NSObject

5. 查找的终点:两个结果

  • 成功:在某一级父类找到了 walk。Runtime 会将该方法地址写入最初接收消息的那个类cache 中(方便下次调用),然后执行代码。
  • 失败:追溯到 NSObject 且其 superclassnil 后依然没找到。此时,程序并不会立即崩溃,而是进入 “消息转发(Message Forwarding)” 流程(即我之前提到的三次自救机会)。

6. 一个特殊的“横跳”:根类逻辑

在继承链的最顶端有一个非常有趣的 Smalltalk 特性:

  • 根元类(Root Meta Class)superclass 指向 根类(Root Class,即 NSObject
  • 效果:如果你对一个类对象调用了一个它没有的类方法,但 NSObject 里有一个同名的实例方法,Runtime 最终能找到并执行它。这保证了所有类对象都能响应 NSObject 定义的基础行为(如 description)。

总结流程图

[实例]isa[当前类]superclass[父类]superclass[根类]找不到消息转发[实例] \xrightarrow{isa} [当前类] \xrightarrow{superclass} [父类] \xrightarrow{superclass} [根类] \to 找不到 \to 消息转发