准备
- objc4-781源码 https://pan.baidu.com/s/1h33XksmvMAvmojxgTWKi9Q 提取码: cfei
1. cache_t是什么?
这里先给一个比较模糊的答案,cache_t
是一个结构体数据类型,将缓存中的函数方法的集合赋值给cache_t
所声明的变量。
先找到一个切入点。我们知道在objc_class
中存在一个cache_t
。
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
....
....
}
打开我们的项目,在LGPerson中随便写4个方法和实现,通过lld输出我们的cache_t
@interface LGPerson : NSObject
-(void)test01;
-(void)test02;
-(void)test03;
-(void)test04;
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *p = [LGPerson alloc];
[p test01];
[p test02];
[p test03];//断点
[p test04];
NSLog(@"%@",p);
}
return 0;
}
至于test1,test2,test3,我就不演示出来了。通过p $2.buckets()[0]这种方式可以得到。由此可见 cache_t中确实存在我们定义的方法。
2. cache_t的追踪
cache_t的结构体
struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
explicit_atomic _buckets;
explicit_atomic _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
explicit_atomic _maskAndBuckets;
mask_t _mask_unused;
static constexpr uintptr_t maskShift = 48;
static constexpr uintptr_t maskZeroBits = 4;
static constexpr uintptr_t maxMask = ((uintptr_t)1 << (64 - maskShift)) - 1;
static constexpr uintptr_t bucketsMask = ((uintptr_t)1 << (maskShift - maskZeroBits)) - 1;
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
explicit_atomic _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;
#if __LP64__
uint16_t _flags;
#endif
uint16_t _occupied;
....
....
}
在cache_t
结构体中有一个_occupied
,在运行函数的时候,这里的_occupied+1,通过cache_t
结构体里面有一个函数incrementOccupied()
追踪到在cache_t::insert
中有_occupied+1操作。
cache_t::insert
void cache_t::insert(Class cls, SEL sel, IMP imp, id receiver)
{
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;
do {
if (fastpath(b[i].sel() == 0)) {
incrementOccupied();
b[i].set(sel, imp, cls);
return;
}
if (b[i].sel() == sel) {
return;
}
} while (fastpath((i = cache_next(i, m)) != begin));
cache_t::bad_cache(receiver, (SEL)sel, cls);
}