窥探struct objc_class的结构
struct objc_class{
Class isa;
Class superclass;
cache_t cache; // 方法缓存
class_data_bits_t bits; // 用于获取具体的类信息
}
Class内部结构中有个方法缓存(cache_t
),用散列表(哈希表)来缓存曾经调用过的方法,可以提供方法的查找速度
struct cache_t {
struct bucket_t *_buckets; // 散列表,是一个bucket_t结构体类型的,数组
mask_t _mask; // 散列表的长度-1,比如散列表长度为10,那么他就是9
mask_t _occupied; // 已经缓存的方法数量
};
散列表的结构体类型,类似字典
struct bucket_t {
cache_key_t _key; // SEL作为key
IMP _imp; // 函数的内存地址
};
如下
- (void)test;
_key = @selector(test)
_imp = test的地址
这就是为什么Objective-C不支持同名方法的原因
Person *p = [[Person alloc] init];
[person test];
[person test];
如果方法的缓存表中已经缓存了方法
[person test]
通过给对象p
发送消息objc_msgSend(person,@selector(test))
,通过实例对象的isa找到类对象Person
,在类对象里面通过方法缓存表,通过遍历,然后找到方法的key
找对应的key
value
,也就是方法的内存地址
这里是key
,通过算法位运算上一个值_mask
,找到对应的索引
// 2就是索引 @selector(test1) & _mask = 2
通过案例来证明有方法的缓存
把一个对象强制转换成C++代码仿写的dx_objc_class
类型查看
如图,断点之前,默认方法缓存数量是1(猜测是对象的init
方法),断点过后,_occupied
是 2,碰巧可以看到缓存的方法名
索引 | bucket_t |
---|---|
0 | NULL |
1 | bucket_t (_key = @selector(test1),_imp) |
2 | bucket_t (_key = @selector(test2),_imp) |
… | … |
散列表为什么效率比数组高?
空间换时间
哈希表原理: 给一个key
,通过一个算法(比如java里面是余%
上一个值,苹果是位运算&上一个值)算出一个索引index
.