方法缓存
问题
ObjC 的方法缓存是如何工作的?
答案
cache_t 结构
struct cache_t {
struct bucket_t *_buckets; // 散列表
mask_t _mask; // 容量 - 1(用于 & 运算)
uint16_t _occupied; // 已使用数量
};
struct bucket_t {
SEL _sel; // 方法名
IMP _imp; // 函数指针
};
查找过程
缓存扩容
- 初始容量 4
- 使用率超过 3/4 时扩容为 2 倍
- 扩容时清空旧缓存(而非迁移)
为什么扩容时清空?
因为缓存基于开放寻址法,扩容后 mask 变化,hash 结果不同,旧数据位置不再正确。且方法会在后续调用时重新缓存(局部性原理)。
常见面试问题
Q1: 为什么用散列表而不是数组?
答案:散列表查找时间复杂度 ,而数组遍历是 。objc_msgSend 是所有方法调用的入口,频率极高,必须保持极致性能。
Q2: objc_msgSend 为什么用汇编实现?
答案:objc_msgSend 是调用频率最高的函数。汇编可以精确控制寄存器、避免函数调用开销、尾调用优化,比 C 编译结果更快。