Cache_t初識(shí)
我們?cè)谇懊鎸?duì)類(lèi)的結(jié)構(gòu)探索中知道了類(lèi)結(jié)構(gòu)體成員如下
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
...
}
我們通過(guò)地址偏移探索知道在bits中包含了類(lèi)的屬性和方法,那么cache_t cache又是什么呢?
從名字上我們可以簡(jiǎn)單的認(rèn)知就是緩存,那究竟是不是緩存呢?它到底緩存了類(lèi)中的什么信息呢?我們接下來(lái)就探索來(lái)cache_t。
Cache_t源碼探索
還是老規(guī)矩,從源碼下手分析其結(jié)構(gòu),我們來(lái)到其源碼的地方如下:
struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
explicit_atomic<struct bucket_t *> _buckets;
explicit_atomic<mask_t> _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
explicit_atomic<uintptr_t> _maskAndBuckets;
mask_t _mask_unused;
// How much the mask is shifted by.
static constexpr uintptr_t maskShift = 48;
// Additional bits after the mask which must be zero. msgSend
// takes advantage of these additional bits to construct the value
// `mask << 4` from `_maskAndBuckets` in a single instruction.
static constexpr uintptr_t maskZeroBits = 4;
// The largest mask value we can store.
static constexpr uintptr_t maxMask = ((uintptr_t)1 << (64 - maskShift)) - 1;
// The mask applied to `_maskAndBuckets` to retrieve the buckets pointer.
static constexpr uintptr_t bucketsMask = ((uintptr_t)1 << (maskShift - maskZeroBits)) - 1;
// Ensure we have enough bits for the buckets pointer.
static_assert(bucketsMask >= MACH_VM_MAX_ADDRESS, "Bucket field doesn't have enough bits for arbitrary pointers.");
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
// _maskAndBuckets stores the mask shift in the low 4 bits, and
// the buckets pointer in the remainder of the value. The mask
// shift is the value where (0xffff >> shift) produces the correct
// mask. This is equal to 16 - log2(cache_size).
explicit_atomic<uintptr_t> _maskAndBuckets;
mask_t _mask_unused;
static constexpr uintptr_t maskBits = 4;
static constexpr uintptr_t maskMask = (1 << maskBits) - 1;
static constexpr uintptr_t bucketsMask = ~maskMask;
#else
#error Unknown cache mask storage type.
#endif
#if __LP64__
uint16_t _flags;
#endif
uint16_t _occupied;
...
}
從源碼我們可以看出cache_t依然是一個(gè)結(jié)構(gòu)體,在里面做了諸多判斷,我們首先弄清楚這些判斷是什么意思,點(diǎn)進(jìn)其中一個(gè)發(fā)現(xiàn)
#if defined(__arm64__) && __LP64__
#define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_HIGH_16 //真機(jī)
#elif defined(__arm64__) && !__LP64__
#define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_LOW_4 //真機(jī) 非64位
#else
#define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_OUTLINED //MacOS、模擬器
#endif
顯然這是對(duì)我們的架構(gòu)進(jìn)行了區(qū)分??梢钥吹皆谡鏅C(jī)中把mask和buckets合并到一起為_maskAndBuckets。
通過(guò)源碼得到cache_t包含了以下成員
buckets探索
點(diǎn)進(jìn)buckets可以看到下面信息
#if __arm64__
explicit_atomic<uintptr_t> _imp;
explicit_atomic<SEL> _sel;
#else
explicit_atomic<SEL> _sel;
explicit_atomic<uintptr_t> _imp;
#endif

對(duì)真機(jī)和非真機(jī)進(jìn)行了判斷,我們可以看到兩個(gè)很重要的東西imp和sel,那么這是不是代表里面緩存類(lèi)類(lèi)的方法呢?同樣我們可以通過(guò)地址偏移去分析。首先還是新建一個(gè)類(lèi)SYPerson,添加個(gè)方法
- (void)helloWorld;
- (void)sayGoodJo;
//調(diào)用
SYPerson *person = [[SYPerson alloc]init];
[person helloWorld];
在方法調(diào)用前我們打下斷點(diǎn)就行l(wèi)ldb調(diào)試打印如下
//獲取類(lèi)的首地址
(lldb) p/x pClass
(Class) $0 = 0x0000000100002420 CCPerson
//地址偏移0x10 即16位打印出cache_t地址
(lldb) p (cache_t *)0x0000000100002430
(cache_t *) $1 = 0x0000000100002430
//打印cache_t信息
(lldb) p *$1
(cache_t) $2 = {
_buckets = {
std::__1::atomic<bucket_t *> = 0x0000000100794100 {
_sel = {
std::__1::atomic<objc_selector *> = ""
}
_imp = {
std::__1::atomic<unsigned long> = 3265552
}
}
}
_mask = {
std::__1::atomic<unsigned int> = 3
}
_flags = 32784
_occupied = 1
}
//調(diào)用buckets()方法
(lldb) p $2.buckets()
(bucket_t *) $3 = 0x0000000100794100
//打印buckets中的信息
(lldb) p *$3
(bucket_t) $4 = {
_sel = {
std::__1::atomic<objc_selector *> = ""
}
_imp = {
std::__1::atomic<unsigned long> = 3265552
}
}
//打印sel,發(fā)現(xiàn)了初始化的init方法
(lldb) p $4.sel()
(SEL) $5 = "init"
//試圖打印其它方法
(lldb) p *($3+1)
(bucket_t) $6 = {
_sel = {
std::__1::atomic<objc_selector *> = (null) //發(fā)現(xiàn)為空,說(shuō)明只有一個(gè)init方法
}
_imp = {
std::__1::atomic<unsigned long> = 0
}
}
//接下來(lái)執(zhí)行一個(gè)實(shí)例方法打印
2020-09-19 18:59:40.848267+0800 KCObjc[8767:115568] SYPerson -- -[CCPerson helloWorld]
//再次打印
(lldb) p *($3+1)
(bucket_t) $7 = {
_sel = {
std::__1::atomic<objc_selector *> = ""
}
_imp = {
std::__1::atomic<unsigned long> = 10496
}
}
//打印sel,這里可以看出在執(zhí)行完helloWorld方法后在cache中就可以找到了,說(shuō)明已經(jīng)緩存進(jìn)去了
(lldb) p $7.sel()
(SEL) $8 = "helloWorld"
//打印imp
(lldb) p $7.imp(pClass)
(IMP) $9 = 0x0000000100000d20 (KCObjc`-[CCPerson helloWorld])
通過(guò)lldb調(diào)試發(fā)現(xiàn)init和helloWorld都加入了緩存。驗(yàn)證了之前所說(shuō)的buckets中緩存了方法.
_occupied & _mask
- _occupied表示哈希表中 sel-imp 的占用大小 (即可以理解為分配的內(nèi)存中已經(jīng)存儲(chǔ)了sel-imp的的個(gè)數(shù)),
- _mask是指掩碼數(shù)據(jù),用于在哈希算法或者哈希沖突算法中計(jì)算哈希下標(biāo),其中mask 等于capacity - 1
cache_t在下層通過(guò)系統(tǒng)的算法分配內(nèi)存空間時(shí)候會(huì)根據(jù)_occupied的值增加進(jìn)行擴(kuò)容,擴(kuò)容后會(huì)將原來(lái)的內(nèi)存都清除,重新開(kāi)辟內(nèi)存。