iOS底层系列11 -- 类的cache成员分析

2021-02-20  本文已影响0人  YanZi_33
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;
#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;
#else
#error Unknown cache mask storage type.
#endif
    
#if __LP64__
    uint16_t _flags;
#endif
    uint16_t _occupied;

    方法省略......
};
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
}

根据测试代码,通过LLDB调试来分析cache_t的工作原理

@interface YYPerson : NSObject

@property(nonatomic,copy)NSString *name;
@property(nonatomic,assign)NSInteger weight;

- (void)walk;
- (void)eat;
- (void)say;
- (void)speak;
- (void)write;

+ (void)sing;
+ (void)work;

@end
@implementation YYPerson

- (void)walk{
    NSLog(@"%s",__func__);
}

- (void)eat{
    NSLog(@"%s",__func__);
}

- (void)say{
    NSLog(@"%s",__func__);
}

- (void)speak{
    NSLog(@"%s",__func__);
}

- (void)write{
    NSLog(@"%s",__func__);
}

+ (void)sing{
    NSLog(@"%s",__func__);
}

+ (void)work{
    NSLog(@"%s",__func__);
}

@end
Snip20210220_26.png
\color{red}{当断点停在第17行,YYPerson类/实例对象未调用方法,此时控制台打印结果如下:}
Snip20210220_27.png
\color{red}{调用walk方法,断点停在第18行,此时控制台打印结果如下:}
Snip20210220_29.png Snip20210220_30.png
\color{red}{接着调用eat方法,断点停在第19行,此时控制台打印结果如下:}
Snip20210220_31.png Snip20210220_32.png
\color{red}{接着调用say方法,断点停在第20行,此时控制台打印结果如下:}
Snip20210220_33.png Snip20210220_34.png Snip20210222_38.png
void cache_t::incrementOccupied() 
{
    _occupied++;
}
Snip20210222_39.png
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());
    
    //_occupied 自增量从0开始;
    mask_t newOccupied = occupied() + 1;
    unsigned oldCapacity = capacity(), capacity = oldCapacity;
    //当自增量=0时,创建缓存空间
    if (slowpath(isConstantEmptyCache())) {
        //capacity = 4(1<<2 => 100)
        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.
        printf("xxxxx");
    }
    //存储空间两倍扩容,会清除先前的存储空间,然后开辟新的存储空间
    else {
        capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;
        if (capacity > MAX_CACHE_SIZE) {
            capacity = MAX_CACHE_SIZE;
        }
        reallocate(oldCapacity, capacity, true);
    }

    //b -- 可看成是缓存bucket结构体的哈希表
    bucket_t *b = buckets();
    //掩码 (buckets容量 - 1) 可保证哈希函数计算出来的哈希索引值 不会出现越界
    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<Atomic, Encoded>(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);
}
void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity, bool freeOld)
{
    bucket_t *oldBuckets = buckets();
    bucket_t *newBuckets = allocateBuckets(newCapacity);

    // Cache's old contents are not propagated. 
    // This is thought to save cache memory at the cost of extra cache fills.
    // fixme re-measure this

    ASSERT(newCapacity > 0);
    ASSERT((uintptr_t)(mask_t)(newCapacity-1) == newCapacity-1);

    setBucketsAndMask(newBuckets, newCapacity - 1);
    
    if (freeOld) {
        cache_collect_free(oldBuckets, oldCapacity);
    }
}
@interface YYPerson : NSObject

@property(nonatomic,copy)NSString *name;
@property(nonatomic,assign)NSInteger weight;

- (void)code1;
- (void)code2;
- (void)code3;
- (void)code4;
- (void)code5;
- (void)code6;
- (void)code7;
- (void)code8;

+ (void)sing;
+ (void)work;

@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //_occupied=0
        YYPerson *person = [YYPerson alloc];
        Class cls = [YYPerson class];
        //_occupied=1/capacity=4
        [person code1];
        //_occupied=2/capacity=4
        [person code2];
        
        ///cache空间2倍扩容,先前cache的方法被清除了,_occupied=1/capacity=8
        [person code3];
        
        //_occupied=2/capacity=8
        [person code1];
        //_occupied=3/capacity=8
        [person code2];
        //_occupied=4/capacity=8
        [person code4];
        //_occupied=5/capacity=8
        [person code5];
        
        ///_occupied=1/capacity=16 再次扩容
        [person code6];
        
        //_occupied=2/capacity=16
        [person code7];
        //_occupied=3/capacity=16
        [person code8];
        
        [YYPerson sing];
        [YYPerson work];
        
        NSLog(@" class = %@",NSStringFromClass(cls));
    }
    return 0;
}

\color{red}{当前断点停在[person code3]所在行} LLDB调试结果如下:

Snip20210222_40.png

\color{red}{过掉[person code3]的断点,停在[person code1]所在行} LLDB调试结果如下:

Snip20210222_41.png Snip20210222_42.png Snip20210222_44.png
上一篇 下一篇

猜你喜欢

热点阅读