美文网首页
06-将手伸进objc_class中的cache, 看看我们调用

06-将手伸进objc_class中的cache, 看看我们调用

作者: luin4 | 来源:发表于2020-09-20 17:46 被阅读0次

  • 我们都知道OC中属性存储数据信息的, 方法的功能修改属性的数据.
  • 在前面我们分析过objc_class结构体(里面存储类的信息), 里面有继承过来的isa(指向元类), 有superClass, 有bits(存储属性, 实例方法, 代理, ro里有成员变量)结构体
  • cache结构体里面存储的是什么呢?

1: 我们先根据源码梳理下objc_class的结构图

objc_class结构树-c1342

2: 接下来我们来通过指针偏移试着看看cache里存储的是什么

cache查找LLDB流程, 图是月月的!

关于缓存占用量的计算,有以下几点说明:

  • buckets() 是个列表, 怎么查找多个呢? 图是月月的!

    • 利用指针偏移, 数组的首地址第一个元素的地址

    例: *($4 + 1)

    • 利用列表特性

    例: $3.buckets()[1]

  • 经过lldb打印查看, 我们可以确认cache里存储的是方法缓存

  • alloc申请空间时,此时的对象已经创建,调用的实例方法,都是shioccupied+1, 扩容后会清空buckets, occupied为置为0

  • 当有属性赋值时,会隐式调用set方法,occupied也会增加


3: 我们详细的看下cache的结构

struct cache_t {//只复制了部分重要信息
//CACHE_MASK_STORAGE_OUTLINED: 模拟器 or macOS环境
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    // explicit_atomic: 原子性, 保证cache增删改差的线程安全
    // 等同于struct bucket_t * _buckets;
    // _buckets: 存放imp和sel
    explicit_atomic<struct bucket_t *> _buckets;
    explicit_atomic<mask_t> _mask; //掩码
//CACHE_MASK_STORAGE_HIGH_16: 64位真机
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    // 真机环境中, buckets和mask掩码存储在一起, 掩码在高16位(通过 << maskShift), buckets 存在剩余位
    explicit_atomic<uintptr_t> _maskAndBuckets;
    mask_t _mask_unused;//暂时没有用到, 猜测没开发完, 不管它
    
    static constexpr uintptr_t maskShift = 48;
    
    //掩码后的其他位必须为零。 msgSend
    //利用这些附加位来构造值
    //在一条来自_maskAndBuckets的指令中`mask << 4`。
    static constexpr uintptr_t maskZeroBits = 4;
    
    // The largest mask value we can store.
    static constexpr uintptr_t maxMask = ((uintptr_t)1 << (64 - maskShift)) - 1;
    
    // 应用于`_maskAndBuckets`的掩码,以获取存储桶指针。
    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.");
//非64为真机, 因为iOS9之后废弃32位, 所以我们不研究它
#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;

    static constexpr uintptr_t maskBits = 4;
    static constexpr uintptr_t maskMask = (1 << maskBits) - 1;
    static constexpr uintptr_t bucketsMask = ~maskMask;
#else
#error Unknown cache mask storage type.
#endif

#if __LP64__
    uint16_t _flags;
#endif
    uint16_t _occupied;

public:
    static bucket_t *emptyBuckets();
    //重点可以获取buckets列表
    struct bucket_t *buckets();
    mask_t mask();// 获取我们的掩码(也可以理解为开辟最大空间)
    mask_t occupied();// 记录当前缓存的方法数量
    void incrementOccupied();// 操作`occupied++`, 即新插入一个bucket
    void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
    void initializeToEmpty();

    unsigned capacity();// 容量
    
    static struct bucket_t * endMarker(struct bucket_t *b, uint32_t cap);
    // 重新开辟空间, 一般在内存满3/4时扩容后调用
    void reallocate(mask_t oldCapacity, mask_t newCapacity, bool freeOld);
    // 插入新的bucket
    void insert(Class cls, SEL sel, IMP imp, id receiver);


4: 总结: 我们来梳理下cache的工作流程

cache 缓存bucket流程图

疑问解答 --Style_月月

  • 1、_mask是什么?

    _mask是指掩码数据,用于在哈希算法或者哈希冲突算法(cache_next)中计算哈希 下标,其中mask等于capacity(内存总容量) - 1

  • 2、_occupied 是什么?

    • _occupied表示哈希表中 sel-imp 的占用大小 (即可以理解为分配的内存中已经存储了sel-imp的的个数),

    • alloc后, 调用的实例方法都会导致occupied变化, 包括属性隐式实现的set方法

  • 3、为什么随着方法调用的增多,其打印的occupied 和 mask会变化?

    因为在cache第一次缓存bucket时,分配的空间是4个,随着方法调用的增多,当存储的bucket个数 超过 capacity(总容量)的3/4, 就会进行capacity翻倍, 并清理旧缓存, 之后继续缓存新调用的实例方法.

  • 4、bucket数据为什么会有丢失的情况?,例如2-7中,只有say3、say4方法有函数指针

    原因是在扩容时,是将原有的内存全部清除了,再重新申请了内存导致的, 见疑问3解答

  • 5、2-7中say3、say4的打印顺序为什么是say4先打印,say3后打印,且还是挨着的,即顺序有问题?

    因为bucket的存储是通过哈希算法-cache_hash计算下标的,其计算的下标有可能已经存储了sel,所以又需要通过哈希冲突-cache_next算法重新计算哈希下标,所以下标并不是固定

    cache_hash实现-c739
cache_next实现-c739
cache缓存bucket流程图.jpg

相关文章

网友评论

      本文标题:06-将手伸进objc_class中的cache, 看看我们调用

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