Cache_t 的整體分析
Cache_t的源碼
在objc/objc-runtime-new源碼下查找結(jié)構(gòu)體cache_t源碼。
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;
...//省略,掩碼用處
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
explicit_atomic<uintptr_t> _maskAndBuckets;
...//省略
#else
#error Unknown cache mask storage type.
#endif
#if __LP64__
uint16_t _flags;
#endif
uint16_t _occupied;
}
那么CACHE_MASK_STORAGE的判斷是什么意義呢?查看CACHE_MASK_STORAGE宏。
arm64表示真機(jī),LP64表示64位結(jié)構(gòu)
#if defined(__arm64__) && __LP64__
#define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_HIGH_16
#elif defined(__arm64__) && !__LP64__
#define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_LOW_4
#else
#define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_OUTLINED//模擬器,MacOS
#endif
lldb獲取cache_t
cache_t 緩存buckets集合
cache_t下有一個(gè)struct bucket_t *buckets();,這是一個(gè)bucket_t結(jié)構(gòu)體集合的指針,我們可以像訪問數(shù)組的方式一下,訪問buckets內(nèi)的內(nèi)每一個(gè)bucket_t。
下面是bucket_t的部分源碼
struct bucket_t {
private:
// IMP-first is better for arm64e ptrauth and no worse for arm64.
// SEL-first is better for armv7* and i386 and x86_64.
#if __arm64__
explicit_atomic<uintptr_t> _imp;
explicit_atomic<SEL> _sel;
#else
explicit_atomic<SEL> _sel;
explicit_atomic<uintptr_t> _imp;
#endif
...//省略
public:
inline SEL sel() const { return _sel.load(memory_order::memory_order_relaxed); }
inline IMP imp(Class cls) const {
...//省略
}
...//省略
}
Cache_t的結(jié)構(gòu)圖

脫離源碼環(huán)境調(diào)試分析
完整代碼
typedef uint32_t mask_t; // x86_64 & arm64 asm are less efficient with 16-bits
struct oc_bucket_t {
SEL _sel;
IMP _imp;
};
struct oc_cache_t {
struct oc_bucket_t * _buckets;
mask_t _mask;
uint16_t _flags;
uint16_t _occupied;
};
struct oc_class_data_bits_t {
uintptr_t bits;
};
struct oc_objc_class {
Class ISA;
Class superclass;
struct oc_cache_t cache; // formerly cache pointer and vtable
struct oc_class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
};
void logClass(Class class){
struct oc_objc_class *lg_pClass = (__bridge struct oc_objc_class *)(class);
NSLog(@"%hu - %u",lg_pClass->cache._occupied,lg_pClass->cache._mask);
for (mask_t i = 0; i<lg_pClass->cache._mask; i++) {
// 打印獲取的 bucket
struct oc_bucket_t bucket = lg_pClass->cache._buckets[I];
NSLog(@"%@ - %p",NSStringFromSelector(bucket._sel),bucket._imp);
}
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [Person alloc];
Class pClass = [Person class]; // objc_clas
[p say1];
[p say2];
logClass(pClass);
[p say3];
[p say4];
logClass(pClass);
[p say5];
[p say6];
logClass(pClass);
}
return 0;
}
輸出結(jié)果
-
調(diào)用兩個(gè)方法時(shí):
兩種不同的方法.png
- 調(diào)用四個(gè)方法時(shí):

Cache_t原理分析
insert方法分析
- 在
Cache_t中找到了void incrementOccupied();函數(shù),對(duì)_occupied進(jìn)行自增
- 全局搜索
incrementOccupied()函數(shù),發(fā)現(xiàn)只在objc-cache文件下cache_t的insert方法有調(diào)用,

-
查看
insert方法,我們發(fā)現(xiàn)進(jìn)入該方法后,就會(huì)對(duì)當(dāng)前的occupied進(jìn)行+1賦值給新的變量newOccupied,-
如果當(dāng)前
buckets為空,則進(jìn)行重新開辟空間reallocate,reallocate中調(diào)用setBucketsAndMask進(jìn)行初始化,_occupied等于0,INIT_CACHE_SIZE為4.void cache_t::setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask) { #ifdef __arm__ // ensure other threads see buckets contents before buckets pointer mega_barrier(); _buckets.store(newBuckets, memory_order::memory_order_relaxed); // ensure other threads see new buckets before new mask mega_barrier(); _mask.store(newMask, memory_order::memory_order_relaxed); _occupied = 0; #elif __x86_64__ || i386 // ensure other threads see buckets contents before buckets pointer _buckets.store(newBuckets, memory_order::memory_order_release); // ensure other threads see new buckets before new mask _mask.store(newMask, memory_order::memory_order_release); _occupied = 0; #else #error Don't know how to do setBucketsAndMask on this architecture. #endif } 如果新增的值 小于容量的
,則什么也不做
否則進(jìn)行擴(kuò)容,重新開辟空間,
_occupied等于0。最后緩存方法,并對(duì)
_occupied進(jìn)行自增。
void cache_t::insert(Class cls, SEL sel, IMP imp, id receiver) { #if CONFIG_USE_CACHE_LOCK cacheUpdateLock.assertLocked(); #else runtimeLock.assertLocked(); #endif ASSERT(sel != 0 && cls->isInitialized()); // Use the cache as-is if it is less than 3/4 full mask_t newOccupied = occupied() + 1; unsigned oldCapacity = capacity(), capacity = oldCapacity; if (slowpath(isConstantEmptyCache())) { // Cache is read-only. Replace it. if (!capacity) capacity = INIT_CACHE_SIZE; reallocate(oldCapacity, capacity, /* freeOld */false); } else if (fastpath(newOccupied + CACHE_END_MARKER <= capacity / 4 * 3)) { // Cache is less than 3/4 full. Use it as-is. } else { capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE; if (capacity > MAX_CACHE_SIZE) { capacity = MAX_CACHE_SIZE; } reallocate(oldCapacity, capacity, true); } bucket_t *b = buckets(); mask_t m = capacity - 1; mask_t begin = cache_hash(sel, m); mask_t i = begin; // Scan for the first unused slot and insert there. // There is guaranteed to be an empty slot because the // minimum size is 4 and we resized at 3/4 full. do { if (fastpath(b[i].sel() == 0)) { incrementOccupied(); b[i].set<Atomic, Encoded>(sel, imp, cls); return; } if (b[i].sel() == sel) { // The entry was added to the cache by some other thread // before we grabbed the cacheUpdateLock. return; } } while (fastpath((i = cache_next(i, m)) != begin)); cache_t::bad_cache(receiver, (SEL)sel, cls); } -
Cache_t流程圖
附上一個(gè)流程圖

_occupied是什么?
_occupied為分配的內(nèi)存中已經(jīng)存儲(chǔ)了sel-imp的的個(gè)數(shù)
_mask是什么?
_mask是指掩碼數(shù)據(jù),mask 等于capacity - 1
為什么buckets會(huì)有丟失?
在reallocate擴(kuò)容時(shí),是將原有的內(nèi)存全部清除了,再重新申請(qǐng)了內(nèi)存導(dǎo)致
為什么順序有問題?
哈希算法計(jì)算下標(biāo),下標(biāo)是隨機(jī)的,并不是固定的.
