美文网首页
0x003 理解iOS weak对象存储原理

0x003 理解iOS weak对象存储原理

作者: 小码农小世界 | 来源:发表于2021-01-20 08:57 被阅读0次

    理解iOS weak对象存储原理

    弱引用对象原理图

    1、案例代码

    void test7() {
        NSObject *objc = [[NSObject alloc] init];
        id __weak obj = objc;
        id __weak obj1 = objc;
        NSLog(@"%@---%@", obj, obj1);
    }
    

    分别声明__weak弱引用对象objobj1

    2、分析__weak原理

    1)使用__weak声明变量时,系统会调用如下方法

    id
    objc_initWeak(id *location, id newObj)
    {
        if (!newObj) {
            *location = nil;
            return nil;
        }
    
        return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
            (location, (objc_object*)newObj);
    }
    

    这里location是弱引用的指针,newObj是被引用的对象。

    2)storeWeak方法

    template <HaveOld haveOld, HaveNew haveNew,
              CrashIfDeallocating crashIfDeallocating>
    static id 
    storeWeak(id *location, objc_object *newObj)
    {
        ASSERT(haveOld  ||  haveNew);
        if (!haveNew) ASSERT(newObj == nil);
    
        Class previouslyInitializedClass = nil;
        id oldObj;
        SideTable *oldTable;
        SideTable *newTable;
     retry:
        if (haveOld) {
            oldObj = *location;
            oldTable = &SideTables()[oldObj];
        } else {
            oldTable = nil;
        }
        if (haveNew) {
            newTable = &SideTables()[newObj];
        } else {
            newTable = nil;
        }
    
        SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
    
        if (haveOld  &&  *location != oldObj) {
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            goto retry;
        }
        if (haveNew  &&  newObj) {
            Class cls = newObj->getIsa();
            if (cls != previouslyInitializedClass  &&  
                !((objc_class *)cls)->isInitialized()) 
            {
                SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
                class_initialize(cls, (id)newObj);
                previouslyInitializedClass = cls;
    
                goto retry;
            }
        }
    
        // Clean up old value, if any.
        if (haveOld) {
            weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
        }
    
        // Assign new value, if any.
        if (haveNew) {
            newObj = (objc_object *)
                weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                      crashIfDeallocating);
            // weak_register_no_lock returns nil if weak store should be rejected
    
            // Set is-weakly-referenced bit in refcount table.
            if (newObj  &&  !newObj->isTaggedPointer()) {
                newObj->setWeaklyReferenced_nolock();
            }
    
            // Do not set *location anywhere else. That would introduce a race.
            *location = (id)newObj;
        }
        else {
            // No new value. The storage is not changed.
        }
        
        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
    
        return (id)newObj;
    }
    

    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(); }
        void forceReset() { slock.forceReset(); }
    
        // Address-ordered lock discipline for a pair of side tables.
    
        template<HaveOld, HaveNew>
        static void lockTwo(SideTable *lock1, SideTable *lock2);
        template<HaveOld, HaveNew>
        static void unlockTwo(SideTable *lock1, SideTable *lock2);
    };
    

    再来看弱引用表weak_table_t,其中有引用实体数组weak_entries

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

    深入看看引用实体weak_table_t,里面有被引用的对象referent,还有弱引用指针地址数组referrers

    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& operator=(const weak_entry_t& other) {
            memcpy(this, &other, sizeof(other));
            return *this;
        }
    
        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;
            }
        }
    };
    

    3)weak_unregister_no_lock分析

    void weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, 
                            id *referrer_id)
    {
        objc_object *referent = (objc_object *)referent_id;
        objc_object **referrer = (objc_object **)referrer_id;
        weak_entry_t *entry;
        if (!referent) return;
        if ((entry = weak_entry_for_referent(weak_table, referent))) {
            remove_referrer(entry, referrer);
            bool empty = true;
            if (entry->out_of_line()  &&  entry->num_refs != 0) {
                empty = false;
            }
            else {
                for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                    if (entry->inline_referrers[i]) {
                        empty = false; 
                        break;
                    }
                }
            }
    
            if (empty) {
                weak_entry_remove(weak_table, entry);
            }
        }
    }
    

    删除已有的弱引用指针

    4)weak_register_no_lock分析,主要代码如下:

        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);
        }
    

    在弱引用表对应的数组中查找,如果有就append_referrer,否则新建weak_entry_t后,再weak_entry_insert到弱引用表的数组中。

    相关文章

      网友评论

          本文标题:0x003 理解iOS weak对象存储原理

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