美文网首页iOS高级进阶ios面试
Runtime底层解析 - 方法缓存 :cache

Runtime底层解析 - 方法缓存 :cache

作者: 南城同學 | 来源:发表于2019-11-11 14:37 被阅读0次
    • Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度。
    struct cache_t {
        struct bucket_t *_buckets; //散列表 (bucket_t, ...)
        mask_t _mask; //散列表的长度 - 1
        mask_t _occupied; //已经缓存的方法数量
    };
    |
    |
    struct bucket_t {
        cache_key_t _key; // SEL作为Key
        IMP _imp;  //函数的内存地址
    };
    
    • 缓存查找:bucket_t * cache_t::find(cache_key_t k, id receiver)
    
    

    散列表(哈希表)

    散列表找元素的原理:
    • f(key) = index
    • 通过一个函数,传入一个key,算出一个索引index
    • 如果这个索引冲突了,那就通过+ 1- 1或某个算法再算一遍,直到不冲突为止。
    应用
    • 在存储bucket_t时,将这个_key&_mask得到一个值,方法就存在相应的位置;
    • 取方法时,也是直接_key&_mask,去对应的值里取bucket_t;
    static inline mask_t cache_hash(cache_key_t key, mask_t mask) 
    {
        return (mask_t)(key & mask);
    }
    
    • 由图表看到好多空间没有使用到,故,这是一种牺牲空间使用效率,来换取查询效率的方法。

    _key&_mask的结果和之前的重复了怎么办 ?

    即: _key&_mask 之后,该位置上已经有bucket_t了或者取出来的bucket_t_key对不上。

    解决:

    以存为例:

    • 得出的值,位置有bucket_t
    • - 1 ,如果还有,继续-1;
    • 值 = 0 ,就直接等于maskmask == 散列表的长度 - 1)。
    static inline mask_t cache_next(mask_t i, mask_t mask) {
        return i ? i-1 : mask;
    }
    

    如果方法过多,散列表原来的长度不够了怎么办?
    • 扩容为原来空间的2倍;
    • 将原缓存清空。
    void cache_t::expand()
    {
        cacheUpdateLock.assertLocked();
        
        uint32_t oldCapacity = capacity();
        uint32_t newCapacity = oldCapacity ? oldCapacity*2 : INIT_CACHE_SIZE; // 2倍
         .........
       reallocate(oldCapacity, newCapacity); //里面有个" cache_collect_free(oldBuckets, oldCapacity);"
    }
    
    

    相关文章

      网友评论

        本文标题:Runtime底层解析 - 方法缓存 :cache

        本文链接:https://www.haomeiwen.com/subject/eoewbctx.html