回顾一下class的结构:
1 | struct objc_class : objc_object { |
不难发现,在objc_class
结构中,有一个cache_t
类型的成员变量cache
。
其结构如下:
1 | struct bucket_t { |
1 | // objc-cache.m |
通过查看cache_t
和bucket_t
的结构以及其实现,可以很清晰的看到类的整个缓存表的内容。
接下来用lldb简单验证下:
1 | // CacheClass.h |
获取CacheObj的isa
1 | (lldb) x/1gx cacheObj |
读取
cache_t
结构体:
1 | (lldb) x/2gx 0x0000000102c2d100+0x10 // 此处+0x10是因为cache是在`objc_class`结构体的第16字节处开始。 |
获取
bucket_t
数组的首地址:
1 | (lldb) p/x ((uintptr_t)1 << (48-4))-1 // 计算 bucketsMask 的值 |
获取
bucket_t
数组的count:
1 | (lldb) p/x (0x0001000281f850c0 >> 48) + 1 // 相当于调用cache_t::capacity()函数 |
输出
bucket_t
数组的内容:
1 | (lldb) x/4gx 0x0000000281f850c0 |
以{IMP,SEL}的结构验证:
1 | (lldb) p (char *)0x00000001b0870410 |
从输出结果可以看出,缓存数组位置是正确的,类CacheClass
缓存了两个方法,分别为:-[NSObject init]
、-[CacheClass cacheMethodA]
。
用代码获取:
1 |
|
需要注意的是,缓存哈希表有一个扩容的过程,当缓存方法超过了哈希表容积时,就会触发扩容,此时,之前的缓存并不会被复制到新的hash表中,而是重新还是缓存!
例如上面的调用修改为如下:
1 | CacheClass *cacheObj = [[CacheClass alloc] init]; |
则输出为:
1 | ============================ |
从上面的输出可以看出,调用方法-[CacheClass cacheMethodB:]
时,触发了缓存表扩容;扩容过程中,它舍弃了原缓存表中的方法,仅缓存了当前方法(-[CacheClass cacheMethodB:]
)。