1-18.【OC】【基础】Method Cache 的作用是什么?

1 阅读3分钟

Method Cache(方法缓存) 的作用可以用四个字概括:变慢为快

如果没有缓存,Objective-C 每一行 [obj message] 都要经历昂贵的方法列表遍历和继承链递归查找。而 Method Cache 存在的唯一目的,就是为了让这种动态的消息机制在性能上无限逼近 C 语言的函数调用


1. 核心作用:消除重复查找的开销

在实际开发中,某个方法一旦被调用,很大记录会在短时间内被再次调用(例如在 for 循环里或 drawRect: 这种高频回调中)。

  • 无缓存时:每次调用都要遍历一次方法列表(Method List)。这是一个线性搜索(O(n)O(n)),如果类的方法很多,或者在父类里,耗时会线性增加。
  • 有缓存时:Runtime 会将查找结果(SELIMP)存入一个哈希表。下次调用时,通过哈希算法直接定位(O(1)O(1))。

2. Method Cache 的物理结构

objc_class 结构体中,cache 成员是一个 cache_t 结构,它本质上包含:

  • Buckets(桶) :一个数组,存储 struct bucket_t(包含 SELIMP)。
  • Mask(掩码) :用于哈希运算,快速定位数组下标。
  • Occupied(已占用量) :记录当前缓存了多少个方法。

3. 它是如何运作的?

  1. 哈希映射:当调用 objc_msgSend 时,取 SEL 的地址与 mask 进行 &(位与)运算,得到一个起始下标。
  2. 碰撞处理:如果该位置已经存了别的方法(哈希冲突),则采用开放寻址法,下标减 1 往回找,直到找到匹配的 SEL 或一个空槽位。
  3. 直接跳转:一旦匹配,直接拿到 IMP 指针并执行。

4. 性能优化中的“副作用”:缓存扩容与清空

为了平衡内存开销和查找速度,缓存并不是无限大的:

  • 动态扩容:当缓存占用率达到临界点(通常是 3/4)时,Runtime 会将缓存空间翻倍(例如从 4 个槽位扩充到 8 个)。

  • 全量清空(Flush)这是最关键的一点。 当缓存扩容时,Runtime 为了保证线程安全和哈希分布的正确性,会清空旧的所有缓存

    后果:你会发现程序运行中,某些原本已经变快的方法会突然产生一次微小的卡顿,因为它们必须重新经历一次“慢速路径”查找并再次填入新缓存。


5. 开发者视角的意义

  • Method Swizzling 的底层联动:当你交换两个方法的实现(Swizzling)时,Runtime 会强制清空对应类的缓存。如果不清空,程序可能还在运行旧的缓存代码,导致 Hook 失败。
  • Category 的覆盖:由于 Category 的方法会在运行时被插入到方法列表的前面,一旦被调用,它就会率先进入 Cache,从而“遮盖”掉类原本的实现。

总结

Method Cache 是 Objective-C 高效运行的“护城河”。它利用了计算机科学中经典的空间换时间策略,将原本复杂的继承链搜索简化成了近乎瞬时的内存取值。