美文网首页
sideTable & weakTable 源码解析 -- 基于

sideTable & weakTable 源码解析 -- 基于

作者: sea777777 | 来源:发表于2020-08-07 19:35 被阅读0次

    总的来说:

    weak_table 是 SideTable 的一个成员变量,避免直接操作 weak_table。

    根据当前对象指针,做一定偏移,找到对应的数组(SideTables)索引,再根据索引取>出这个 SideTable 。
    你可以理解为key是对象指针,value就是 SideTable。
    也就是说,一个对象,对应一个 SideTable,一个 SideTable 对应一个 weak_table,
    一个weak_table 存储一个哈希表,key:当前对象,value:weak 引用

    下面的代码,都已经去掉了无关紧要的,留下的都是重点部分:

    首先看一下 SideTables , 实际上内部维护了一个Map(key:指针 —— value:SideTable)

    array :存储的就是SideTable 数组
    indexForPointer 方法,这个方法根据实例的指针地址,返回它在 array 中的索引,从而>找到 SideTable
    由此我们知道:每个实例,SideTables 都会帮这个实例存储一个SideTable

    static objc::ExplicitInit<StripedMap<SideTable>> SideTablesMap;
    
    static StripedMap<SideTable>& SideTables() {
        return SideTablesMap.get();
    }
    

    StripedMap<T> is a map of [void* : T]

    class StripedMap {
        enum { StripeCount = 8 };
    
        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:
        T& operator[] (const void *p) { 
            return array[indexForPointer(p)].value; 
        }
        const T& operator[] (const void *p) const { 
            return const_cast<StripedMap<T>>(this)[p]; 
        }
        
    #if DEBUG
        StripedMap() {
            // Verify alignment expectations.
            uintptr_t base = (uintptr_t)&array[0].value;
            uintptr_t delta = (uintptr_t)&array[1].value - base;
            ASSERT(delta % CacheLineSize == 0);
            ASSERT(base % CacheLineSize == 0);
        }
    #else
        constexpr StripedMap() {}
    #endif
    };
    


    每个SideTable 都存储了一个 weak_table 、引用计数的map、以及lock

    struct SideTable {
        spinlock_t slock;
        RefcountMap refcnts;
        weak_table_t weak_table;
    
        void lock() { slock.lock(); }
        void unlock() { slock.unlock(); }
        void forceReset() { slock.forceReset(); }
    };
    



    然后看weak_table:这是一个全局的weak 引用表。
    weak_entries:存储 weak_entry_t 列表
    num_entries :weak 引用的个数
    mask :用来做哈希表的mask

    struct weak_table_t {
        weak_entry_t *weak_entries;
        size_t    num_entries;
        uintptr_t mask;
        uintptr_t max_hash_displacement;
    };
    


    weak_entry_t: 存储所有指向实例的weak引用

    referent:当前实例对象指针:object point
    inline_referrers: weak 变量指针(weak reference ) 集合,引用低于4个的时候,用这个结构存储
    referrers:weak 变量指针(weak reference ) 集合,大于4个引用时候用这个存储

    这里使用了 union 来共享内存,可以节约内存的使用
    当 out_of_line_ness == REFERRERS_OUT_OF_LINE 时,使用 referrers 来存储weak >变量引用
    当 out_of_line_ness != REFERRERS_OUT_OF_LINE 时,使用 inline_referrers 来存>储weak 变量引用

    mask :一般用指针的地址 & mask 来找到对应的数组索引,而mask 一般为数组的长度

    struct weak_entry_t {
        DisguisedPtr<objc_object> referent;
        union {
            struct {
                weak_referrer_t *referrers;
                uintptr_t        out_of_line_ness : 2;
                uintptr_t        num_refs : PTR_MINUS_2;
                uintptr_t        mask;
                uintptr_t        max_hash_displacement;
            };
            struct {
                // out_of_line_ness field is low bits of inline_referrers[1]
                weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
            };
        };
    
        bool out_of_line() {
            return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
        }
    
        weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
            : referent(newReferent)
        {
            inline_referrers[0] = newReferrer;
            for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
                inline_referrers[i] = nil;
            }
        }
    };
    


    storeWeak 这个方法用来存储新的指向当前实例的 weak 变量。
    可以看到,先从 SideTables 拿到对应的 SideTable,
    然后调用 weak_register_no_lock 把 newObj 存到了weak_table 里

    static id 
    storeWeak(id *location, objc_object *newObj)
    {
        SideTable *newTable;
    
        // Acquire locks for old and new values.
        // Order by lock address to prevent lock ordering problems. 
        // Retry if the old value changes underneath us.
     retry:
        
        newTable = &SideTables()[newObj];
        
        newObj = (objc_object *) weak_register_no_lock(&newTable->weak_table, (id)newObj, location,  crashIfDeallocating);
    
        // Do not set *location anywhere else. That would introduce a race.
        *location = (id)newObj;
    
        return (id)newObj;
    }
    



    weak_entry_for_referent 这个方法就不写了:实际就是找当前实例对象所属的 weak_entry_t

    这个方法是遍历 weak_table->weak_entries ,然后检查 weak_entries[index].referent >== referent
    如果找到了,就返回 weak_table->weak_entries[index]

    接下来我们看另外一个很重要的方法:append_referrer:

    /** 
     * Registers a new (object, weak pointer) pair. Creates a new weak
     * object entry if it does not exist.
     * 
     * @param weak_table The global weak table.
     * @param referent The object pointed to by the weak reference.
     * @param referrer The weak pointer address.
     */
    id 
    weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
                          id *referrer_id, bool crashIfDeallocating)
    {
        objc_object *referent = (objc_object *)referent_id;// 当前的实例对象指针
        objc_object **referrer = (objc_object **)referrer_id;//weak 变量的指针的指针
    
        // now remember it and where it is being stored
        weak_entry_t *entry;
        if ((entry = weak_entry_for_referent(weak_table, referent))) {
            append_referrer(entry, referrer);
        } 
        else {
            weak_entry_t new_entry(referent, referrer);
            weak_grow_maybe(weak_table);
            weak_entry_insert(weak_table, &new_entry);
        }
    
        return referent_id;
    }
    


    把新的weak 变量引用,加到 entry里:

    优先使用 inline_referrers 存储 weak 引用
    如果inline_referrers 存储满了,则把 inline_referrers 拷贝到 referrers 里,并且以后使用 referrers 来存储,并标记:out_of_line_ness = REFERRERS_OUT_OF_LINE; 这样下次默认就使用 referrers 来存储。

    如果存储的引用数量超过了 3/4 ,则把 referrers 扩容 二倍,再进行存储。

    存储referrers 的阶段,先把指针做一定偏移,然后 & mask,找到要存储在数组里的位置: index,
    如果这个index 已经存储了值,那么再次偏移、然后 & mask ,递归来找,直到找到一个空的位置为止。
    最后把 weak 引用存到这个数组的 index 位置里。

    当然,移除 weak 引用,和 append 引用是相似的道理,这里就不再赘述了。

    static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
    {
        if (! entry->out_of_line()) {
            // Try to insert inline.
            for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                if (entry->inline_referrers[i] == nil) {
                    entry->inline_referrers[i] = new_referrer;
                    return;
                }
            }
    
            // Couldn't insert inline. Allocate out of line.
            weak_referrer_t *new_referrers = (weak_referrer_t *)
                calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
            // This constructed table is invalid, but grow_refs_and_insert
            // will fix it and rehash it.
            for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                new_referrers[i] = entry->inline_referrers[i];
            }
            entry->referrers = new_referrers;
            entry->num_refs = WEAK_INLINE_COUNT;
            entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;
            entry->mask = WEAK_INLINE_COUNT-1;
            entry->max_hash_displacement = 0;
        }
    
    
        if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
            return grow_refs_and_insert(entry, new_referrer);
        }
        size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
        size_t index = begin;
        size_t hash_displacement = 0;
        while (entry->referrers[index] != nil) {
            hash_displacement++;
            index = (index+1) & entry->mask;
            if (index == begin) bad_weak_table(entry);
        }
        if (hash_displacement > entry->max_hash_displacement) {
            entry->max_hash_displacement = hash_displacement;
        }
        weak_referrer_t &ref = entry->referrers[index];
        ref = new_referrer;
        entry->num_refs++;
    }
    

    相关文章

      网友评论

          本文标题:sideTable & weakTable 源码解析 -- 基于

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