在 Objective-C 中,实例、类、元类之间的关系被称为 “isa 指针走线图” 。这套设计是 Objective-C 能够实现“万物皆对象”和“运行时动态查找方法”的核心。
它们三者的关系可以用身份(是什么) 、**职责(存什么)和指向(找谁)**来拆解:
1. 核心定义与角色
| 对象类型 | 它是谁? | 存什么?(职责) |
|---|---|---|
| 实例对象 (Instance) | 类生成的具体个体。 | 成员变量的具体值。 |
| 类对象 (Class) | 描述实例的模板。 | 实例方法列表、属性定义、协议。 |
| 元类对象 (Meta Class) | 描述类的类。 | 类方法列表(以 + 开头的方法)。 |
2. 三者的递进关系(isa 走线)
为了找到要执行的代码,Runtime 遵循一套严格的查找路径。你可以把 isa 想象成“向上级请示”的线:
-
实例对象的
isa指向 类对象:当你调用
[obj hello](实例方法)时,通过实例的isa去类对象里找。 -
类对象的
isa指向 元类对象:当你调用
[User alloc](类方法)时,通过类对象的isa去元类对象里找。 -
元类对象的
isa指向 根元类 (Root Meta Class) :通常是
NSObject的元类。 -
根元类的
isa指向 它自己:这是查找链的终点。
3. 继承关系(Superclass 走线)
除了 isa 指向,它们还通过 superclass 指针维持着继承体系:
- 类对象的 superclass 指向父类的类对象。
- 元类对象的 superclass 指向父类的元类对象。
- 特殊的闭环: 根元类(Root Meta Class)的
superclass指向 根类(Root Class,即 NSObject) 。
这意味着一个有趣的现象: 如果你给
NSObject写了一个实例方法(-),但你却对某个类调用了同名的类方法(+),如果元类里找不到,它最终会沿着继承链找到NSObject的实例方法并执行。
4. 为什么要搞得这么复杂?(引入元类的意义)
Objective-C 追求逻辑的一致性:
- 在 Smalltalk 的哲学里,“消息发送”必须有一个接收者。
- 如果你想调用类方法
[User alloc],那么User这个“类”本身也必须是一个对象。 - 既然类是对象,它就必须是某个更高级的类的实例。这个“更高级的类”就是元类。
通过引入元类,Objective-C 成功地把实例方法和类方法统一成了同一种处理机制:都是去该对象的 isa 指向的地方找方法列表。
总结
- 实例存数据。
- 类存实例方法。
- 元类存类方法。