Cache_t結(jié)構(gòu)分析

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ī)中把maskbuckets合并到一起為_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
類(lèi)結(jié)構(gòu)

對(duì)真機(jī)和非真機(jī)進(jìn)行了判斷,我們可以看到兩個(gè)很重要的東西impsel,那么這是不是代表里面緩存類(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)inithelloWorld都加入了緩存。驗(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)存。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容