Objective-C 类结构全景解析

11 阅读3分钟

从 isa 到 cache,从方法列表到属性列表

一次把「一个 Class 里到底装了什么」讲清楚

在 Runtime 视角下,Objective-C 的 Class 并不是一个抽象概念

而是一块结构严谨、职责清晰的内存结构

本文将围绕 Class 的真实组成,系统讲解:

  • isa 指针到底指向哪
  • cache 为什么决定性能
  • 方法列表、属性列表、协议列表各自干什么
  • 一个类里,除了方法,还存了哪些东西

一、先给结论:一个 Class 里有什么?

从 Runtime 角度,一个类(Class)至少包含以下几大部分:

Class
 ├─ isa
 ├─ superclass
 ├─ cache
 ├─ method list
 ├─ property list
 ├─ protocol list
 ├─ ivar list
 ├─ class_rw_t / class_ro_t
 └─ 元类(Meta Class

下面我们逐一展开。


二、isa —— 类的“身份指针”

1. isa 是什么

  • isa 是一个指针
  • 对象的 isa → Class
  • 类的 isa → Meta Class
instance ──isa──▶ Class ──isa──▶ Meta Class

在 arm64 以后:

  • isa 是 非纯指针(non-pointer isa)

  • 高位存储了:

    • 引用计数信息

    • weak 标志

    • 是否有关联对象

逻辑语义没有变化


三、cache —— 方法调用的性能核心

1. cache 是什么

  • cache 是一个 SEL → IMP 的映射表
  • 存在于 Class 中
  • 用于加速方法查找
cache
 ├─ bucket[SEL → IMP]
 └─ mask / occupied

2. cache 在方法查找中的位置

objc_msgSend 查找顺序:

1️⃣ cache
2️⃣ method list
3️⃣ superclass → 重复 12

cache 永远是第一站。


3. cache 的填充时机

  • cache 是 懒加载

  • 第一次方法调用:

    • cache 未命中

    • method list 找到 IMP

    • 写入 cache

之后同一个 SEL:

直接命中 cache


4. cache 为什么不区分类?

cache 的 key 是:

SEL

但 cache 属于 某一个 Class

因此:

A.fooA 的 cache
B.fooB 的 cache

即使 SEL 相同,也互不干扰。


四、method list —— 方法的“原始数据源”

1. method list 是什么

  • method list 是一个数组
  • 每一项是一个 method_t
method_t
 ├─ SEL name
 ├─ IMP imp
 └─ const char *types

也就是我们熟悉的三要素:

SEL + IMP + Type Encoding


2. method list 的来源

method list 由以下部分合并而来:

  • 类本身实现的方法

  • Category 中的方法

⚠️ Category 的方法:

  • 后加载、前插入
  • 因此可以覆盖原方法

五、property list —— 属性的声明信息

1. property list 是什么

  • 属性列表存的是 声明信息
  • 不是 ivar
  • 不是 getter / setter 的实现
objc_property_t
 ├─ name
 └─ attributes (copy, nonatomic, strong ...)

2. property list 干什么用

  • Runtime 反射

  • KVC / KVO

  • 自动序列化 / ORM

但注意:

方法调用完全不依赖 property list


六、ivar list —— 实例变量的真实布局

1. ivar list 是什么

  • ivar list 描述的是:

    • 成员变量
    • 内存偏移
    • 类型
ivar_t
 ├─ name
 ├─ type
 └─ offset

2. ivar list 与对象内存

instance memory
 ├─ isa
 ├─ ivar1
 ├─ ivar2
  • ivar list 决定对象内存布局
  • 子类 ivar 会追加在父类之后

七、protocol list —— 协议信息

1. protocol list 是什么

  • 存储类遵循的协议

  • 包含:

    • 必选方法

    • 可选方法

主要用于:

  • conformsToProtocol:
  • Runtime 查询

八、class_rw_t / class_ro_t —— 可变与只读区

1. class_ro_t(只读)

  • 编译期确定

  • 存储:

    • 原始方法列表
    • ivar list
    • property list

2. class_rw_t(可写)

  • 运行时动态生成

  • 存储:

    • Category 方法

    • 动态添加的方法

这也是 Category 能“修改类行为”的根本原因。


九、Meta Class —— 类方法的归宿

1. Meta Class 是什么

  • 类方法不是存在 Class 里
  • 而是存在 Meta Class 的 method list 中
[Class foo]
 → 查找 Meta Class 的 cache / method list

十、一张完整 Runtime 结构图(逻辑)

instance
  └─ isa → Class
              ├─ isa → Meta Class
              ├─ superclass
              ├─ cache
              ├─ method list
              ├─ property list
              ├─ ivar list
              ├─ protocol list
              └─ class_rw_t / class_ro_t

十一、终极理解(非常重要)

  • 方法调用性能 = cache 决定

  • 行为修改能力 = method list + rw 区

  • 内存布局 = ivar list 决定

  • 反射能力 = property / protocol 提供

它们各司其职,互不混乱。


十二、一句话总结

Class 是 Runtime 的作战单元:

cache 决定快慢,method list 决定行为,

ivar 决定内存,property 决定语义,

isa 决定你是谁。

理解这一层结构,

你就真正理解了 Objective-C Runtime 的“骨架”。