iOS

iOS - objc-weak

2021-04-10  本文已影响0人  ienos

Objective-C.OpenSource.GitHub

一、 知识补充

1. Tagged Pointer

一个对象在 32 bit CPU 下占 4 byte,在 64bit CPU下是占 8 byte

一个指针在 32 bit CPU下为 4 byte,在 64bit CPU下也是 8 byte

Tagged Pointer 专门用来存储小的对象

通常我们一个正常的对象需要通过指针寻址找到具体的值

类似 NSNumber、NSdata 4 个字节已经够用了,所以在 64 bit 中引入 Tagged Pointer

为了优化内存的使用,Tagged Pointer 即直接将值存储到指针中,8 byte 中 4 byte 存储值 4 byte 存储指针

2. bit-mask

在 objc-weak 中会看到诸如 index = w_hash_pointer(old_referrer) & (entry->mask) 的代码

这里使用的是位屏蔽 bit-mask

>> 例如通过 w_hash_pointer 散列计算得出 Value = 01010101

然后 Mask = 00001111

Mask    = 00001111b
Value   = 01010101b
Result  = 00000101b

可以看出最后我们得到的结果总小于 Mask,意味着 Mask 用来作为 Value 最大值限制

那么 Mask 就应该是 Result 的最大值

二、数据结构

image.png

二、方法解析

>> 1. hash 方法

Function Description
hash_pointer 散列对象指针
w_hash_pointer 散列弱引用

>> weak reference

Function Description
grow_refs_and_insert 扩展表的大小,并且插入新的 weak reference
append_referrer 添加新的 weak reference 到 weak_entry
remove_referrer 在 weak_entry 中移除 weak reference

>> weak_table 表大小

Function Description
weak_resize 重新设置 weak_ table 表的大小,并重新存储 weak_entry
weak_grow_maybe 如果 weak_table 中的 num_entries 超过表大小的 3/4 则 weak_resize 原来的两倍;若原先为 0 则默认为 64
weak_compact_maybe 如果 weak_table 中的 num_entries 小于表大小的 1/16,或者表大小超过 1024 则 weak_resize 缩小为原来的 1/8

>> weak_table 中对 weak_entry 的操作

Function Description
weak_entry_insert 在 weak_table 中插入 weak_entry
weak_entry_remove 在 weak_table 中移除 weak_entry;并调用 weak_compact_maybe
weak_entry_for_referent 在 weak_table 中找到某个对象的 weak_entry

>> weak_table 的 weak reference 操作

Function Description
weak_unregister_no_lock 在 weak_table 中移除某个对象的对应 weak reference
weak_register_no_lock 在 weak_table 中注册某个对象的对应 weak reference
weak_clear_no_lock 清除某个对象对应的所有 weak reference
weak_read_no_lock 获取某个 weak reference 对应的对象

三、结构体解析

1. SideTables

保存的是关于所有对象的表,这里的表分为两级

SideTables 是一个全局静态的表结构,表中保存的是 SideTable,key 对应的是对象的指针

// alignas(StripedMap<SideTable>) 内存对齐方式
alignas(StripedMap<SideTable>) static uint8_t 
    SideTableBuf[sizeof(StripedMap<SideTable>)];

static void SideTableInit() {
    // 在 SideTableBuf 数组首地址中分配 StripedMap<SideTable>
    new (SideTableBuf) StripedMap<SideTable>();
}

// StripedMap<SideTable>& 引用 SideTables() 返回的值
static StripedMap<SideTable>& SideTables() {
    // 强转 SideTableBuf 为 StripedMap<SideTable>* 指针
    return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}

2. StripedMap

实际上也是散列表,一个大小为 8 的散列表

template<typename T>
class StripedMap {

    enum { CacheLineSize = 64 };
    enum { StripeCount = 8 };

    struct PaddedT {
        // 定义类型为 T 的变量 value,内存对齐 CacheLineSize
        T value alignas(CacheLineSize);
    };

    // 定义一个个数为 StripeCount 的 Padded 元素数组
    PaddedT array[StripeCount];

    static unsigned int indexForPointer(const void *p) {
        uintptr_t addr = reinterpret_cast<uintptr_t>(p);
        return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
    }

 public:
    // stripedMap[p] 实质是一个表结构
    // index 其实是指针,通过散列计算获取对应 T 的实例对象
    T& operator[] (const void *p) { 
        return array[indexForPointer(p)].value; 
    }
    const T& operator[] (const void *p) const { 
        return const_cast<StripedMap<T>>(this)[p]; 
    }

};

3. SideTable

一个 SideTable 对应一个 weak_table

struct SideTable {
    spinlock_t slock;
    RefcountMap refcnts;
    weak_table_t weak_table;

    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }

    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }

    void lock() { slock.lock(); }
    void unlock() { slock.unlock(); }
    bool trylock() { return slock.trylock(); }

    // Address-ordered lock discipline for a pair of side tables.

    template<bool HaveOld, bool HaveNew>
    static void lockTwo(SideTable *lock1, SideTable *lock2);
    template<bool HaveOld, bool HaveNew>
    static void unlockTwo(SideTable *lock1, SideTable *lock2);
};

4. weak_table

存储 weak_entry 的结构体

struct weak_table {
    // 存储 `weak_entry` 的数组,索引为 weak_entry->referent 的散列值
    weak_entry_t *weak_entries;
    // weak_entries 数组个数
    size_t  num_entries;
    // weak_entries 的最大索引,用于 bit-mask;当 mask 改变时需要重新计算 weak_entry->referent 散列值
    uintptr_t mask;
    // 最大散列值
    uintptr_t max_hash_displacement;
};

5. weak_entry

weak_entryweak pointer 上存储分为两个部分:

referrer 个数不超过 WEAK_INLINE_COUNT 4 时,使用 inline_referrers,否则使用 referrers

#define WEAK_INLINE_COUNT 4
struct weak_entry_t {

    // 源对象
    DisguisedPtr<objc_object> referent;

    union {

        /* 动态数组 */
        struct {
            weak_referrer_t *referrers;
            // 是否使用 referrers 的标志位,即是否超出 WEAK_INLINE_COUNT 个数
            uintptr_t        out_of_line : 1;
            // referrers 中元素个数
            uintptr_t        num_refs : PTR_MINUS_1;
            // referrers 的最大索引数量,用于 bit-mask;当 mask 改变时需要重新计算 referrer 散列值
            uintptr_t        mask;
            // 最大散列值
            uintptr_t        max_hash_displacement;
        };

        /* 定长数组 */
        struct {
            // out_of_line=0 is LSB of one of these (don't care which)
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    };
};

6. weak_referrer

对象指针的指针,即弱引用

typedef objc_object ** weak_referrer_t;
上一篇下一篇

猜你喜欢

热点阅读