iOS cache_t的研究

准备

  • 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;
}
1.png
2.png

至于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操作。

3.jpg

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);
}
4.jpg

你可能感兴趣的:(iOS cache_t的研究)