窥探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,碰巧可以看到缓存的方法名 demo

索引 bucket_t
0 NULL
1 bucket_t (_key = @selector(test1),_imp)
2 bucket_t (_key = @selector(test2),_imp)

散列表为什么效率比数组高?

空间换时间

哈希表原理: 给一个key,通过一个算法(比如java里面是余%上一个值,苹果是位运算&上一个值)算出一个索引index.