1-14.【OC】【基础】为什么 Objective-C 可以在运行时动态添加方法?

2 阅读3分钟

Objective-C 之所以能在运行时动态添加方法,是因为它在底层将“”设计成了一个可变的数据结构,并且拥有一套基于名称查找而非地址偏移的消息分发系统。

这可以从以下三个层面来透彻理解:

1. 类的本质:一个动态的“字典”

在 C++ 或 Java(非反射部分)中,类的方法在编译后通常变成了一个固定的函数指针表(V-Table)。如果你想加个方法,必须改源代码重新编译。

但在 Objective-C 中,类对象(objc_class)内部维护的是一个方法列表(Method List) 。你可以把它想象成一个动态字典

  • Key(键): 是方法的名称(SEL,选择子)。
  • Value(值): 是代码的具体实现地址(IMP,函数指针)。

既然是“字典”,Runtime 库(底层 C 函数库)就提供了类似 dictionary_add 的操作。class_addMethod 函数的本质就是向这个字典里插入了一个新的 Key-Value 对


2. 消息机制的“解耦”

这是最核心的原因。在 Objective-C 中,调用方法 [obj method] 会被翻译成 objc_msgSend(obj, @selector(method))

  • 编译期:编译器只负责把 method 这个名字变成一个唯一的字符串(SEL)。它不需要知道这个 method 对应的代码在哪,甚至不需要知道它存不存在。
  • 运行时:只有当代码运行到这一行时,Runtime 才会拿着这个名字去类的“字典”里搜。

正是因为“找代码”这个动作发生在运行的那一刻,所以只要你在调用之前把方法塞进那个“字典”里,程序就能跑通。


3. 动态方法解析(自救机制)

Objective-C 甚至在语言层面专门为“动态添加方法”留了后门,即 动态方法解析(Dynamic Method Resolution)

objc_msgSend 在类的方法列表里找不到某个方法时,它不会立即崩溃,而是会触发一个特殊的钩子函数: +resolveInstanceMethod:(或 +resolveClassMethod:)。

在这个函数里,你可以临时写一段逻辑:

“噢,我发现你要调用 fly 方法,但我现在没有。别急,我现在立刻用 class_addMethod 动态给你造一个!”

这就是 Core Data 框架(@dynamic 属性)以及很多**热修复(Hotfix)**框架能够生效的底层逻辑。


4. 具体操作:如何动态添加?

底层函数如下:

Objective-C

// cls: 给哪个类加
// name: 方法名 (SEL)
// imp: 函数指针 (C函数)
// types: 方法签名(描述参数和返回值)
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types);

这个动作会修改类对象中的 bits 数据段。由于类对象(Class Object)在内存中是唯一的且一直存在的,一旦添加成功,所有该类的实例都能立刻“学会”这个新技能。


总结

Objective-C 允许动态添加方法,是因为:

  1. 数据结构支持:类的方法列表是可写的链表/数组。
  2. 查找机制支持:查找行为推迟到了运行那一刻,给“临时加戏”留出了空间。
  3. 设计哲学支持:它继承了 Smalltalk “万物皆对象且可交互”的思想。