在 objc_msgSend 中,方法缓存的查找是通过哈希表(Hash Table)实现的。每个类(Class)都有一个方法缓存(Method Cache),用于存储最近使用的方法实现。使用哈希表可以快速地在缓存中查找方法实现,这大大提高了方法调用的效率。
下面我们详细分析方法缓存查找算法,并结合源码进行重点注释。
方法缓存 (Method Cache) 结构
方法缓存是一个哈希表,存储了方法的选择器(Selector)和方法的实现指针(IMP)。在 Objective-C 的运行时库中,方法缓存的结构定义如下:
struct cache_t {
// 指向缓存条目的指针
struct bucket_t *buckets;
// 缓存的容量
mask_t mask;
// 缓存中的条目数
mask_t occupied;
// 在缓存中查找方法实现
inline IMP find(SEL selector) {
// 获取哈希值
mask_t index = (mask_t)selector & mask;
// 遍历哈希表
for (mask_t i = 0; i <= mask; i++) {
// 获取当前索引
mask_t currIndex = (index + i) & mask;
// 获取缓存条目
bucket_t *bucket = &buckets[currIndex];
// 检查选择器是否匹配
if (bucket->sel == selector || bucket->sel == NULL) {
return bucket->imp;
}
}
// 如果在缓存中未找到实现,返回 NULL
return NULL;
}
};
objc_msgSend 中的方法缓存查找
在 objc_msgSend 函数中,首先会在方法缓存中查找方法实现。下面是相关代码及注释:
// objc_msgSend 函数负责消息发送
void objc_msgSend(id self, SEL _cmd, ...) {
// 获取类对象
Class cls = object_getClass(self);
// 获取方法缓存
cache_t *cache = &cls->cache;
// 在缓存中查找方法实现
IMP imp = cache->find(_cmd);
// 如果找到方法实现,直接返回实现的指针
if (imp) {
return imp;
}
// 如果缓存中没有找到方法实现,调用未缓存的消息发送函数
return _objc_msgSend_uncached(self, _cmd);
}
方法缓存查找算法
方法缓存查找的具体实现是在 cache_t 结构体中的 find 方法中。下面是 find 方法的详细注释:
struct cache_t {
struct bucket_t *buckets;
mask_t mask;
mask_t occupied;
// 在缓存中查找方法实现
inline IMP find(SEL selector) {
// 获取哈希值,使用选择器地址与缓存掩码进行与操作
mask_t index = (mask_t)selector & mask;
// 遍历哈希表,线性探测
for (mask_t i = 0; i <= mask; i++) {
// 计算当前索引
mask_t currIndex = (index + i) & mask;
// 获取当前缓存条目
bucket_t *bucket = &buckets[currIndex];
// 如果选择器匹配,返回方法实现
if (bucket->sel == selector) {
return bucket->imp;
}
// 如果选择器为空,表示未命中,返回 NULL
if (bucket->sel == NULL) {
return NULL;
}
}
// 如果遍历完所有条目仍未找到实现,返回 NULL
return NULL;
}
};
解释说明
-
哈希计算:
- 使用选择器地址与缓存掩码(mask)进行与操作,得到初始索引。这是基于选择器地址的简单哈希函数。
-
线性探测:
- 采用线性探测法(Linear Probing)来解决哈希冲突。即如果初始位置被占用,则继续探测下一个位置,直到找到匹配的选择器或遇到空位(表示未命中)。
-
比较选择器:
- 在查找过程中,如果选择器匹配,则返回对应的方法实现指针(IMP)。
- 如果选择器为空,表示查找未命中,返回
NULL。
小结
通过以上分析可以看出,objc_msgSend 函数在方法缓存中查找方法实现时,采用了基于哈希表的查找算法,并结合线性探测来解决哈希冲突。这种方式能够在大多数情况下快速找到方法实现,从而提高方法调用的效率。
源码中的这些注释和详细说明,有助于我们更好地理解 Objective-C 运行时库中方法缓存查找的具体实现机制。