Ios@IONIC

weak总结

2019-03-20  本文已影响0人  YY_Lee

系统创建了一个全局的哈希表SideTables,它使用对象的内存地址作key,值为SideTable结构体。SideTable维护对象的引用计数器和弱引用表。弱引用表weak_table也是个哈希表,key是weak指针指向对象的地址,value是weak_entry_t对象,weak_entry_t存储着所有指向该对象的weak指针地址数组。

weak 的实现原理可以概括如下:

struct SideTable {
    spinlock_t slock;
    RefcountMap refcnts; // 计数器表
    weak_table_t weak_table; // 弱引用表
}

struct weak_table_t {
    // 弱引用散列表,key是对象地址,value是weak_entry_t对象
    weak_entry_t *weak_entries;
    size_t    num_entries; 
    uintptr_t mask;
    uintptr_t max_hash_displacement;
};

struct weak_entry_t {
    DisguisedPtr<objc_object> referent;//the address of a __weak variable.
    union {
        struct {
            // 某个对象全部的弱引用指针地址
            weak_referrer_t *referrers; 
            //最低有效位,也是标志位。当标志位 0 时,增加引用表指针纬度。
            uintptr_t        out_of_line_ness : 2;
            uintptr_t        num_refs : PTR_MINUS_2;//引用数值。
            uintptr_t        mask;//计数辅助量
            uintptr_t        max_hash_displacement;//hash 元素上限阀值
        };
        struct {
            // out_of_line_ness field is low bits of inline_referrers[1]
            //weak指针数量小于等于4时,存储在inline_referrers中,超过4个才会存储到referrers中
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    };
}

下面是具体函数的实现:

id objc_initWeak(id *location, id newObj)
{
    if (!newObj) {
        *location = nil;
        return nil;
    }

    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object*)newObj);
}

objc_initWeak函数只是做个非空判断,接着调用objc_storeWeak函数:

//如果HaveOld为真,变量有值需要被清空。值可能为空。
//如果HaveNew为真,需要为变量分配一个新值,值可能为空。
//如果CrashIfDeallocating为真,且newObj正在释放或newObj的类不支持弱引用,这个过程停止。如果CrashIfDeallocating为false,存储nil。
enum CrashIfDeallocating {
    DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};
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;//根据weak指针获取对象地址
        oldTable = &SideTables()[oldObj];//根据对象获得对应SideTable
    } else {
        oldTable = nil;
    }
    if (haveNew) {
        newTable = &SideTables()[newObj];//根据对象获得对应SideTable
    } 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(_class_getNonMetaClass(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);
    
        if (newObj  &&  !newObj->isTaggedPointer()) {
            newObj->setWeaklyReferenced_nolock();
        }
        // 之前不要设置 location 对象,这里需要更改指针指向
        *location = (id)newObj;
    }
    else {
        // No new value. The storage is not changed.
    }
    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
    return (id)newObj;
}

清除旧值源码如下,具体清除的步骤在remove_referrer函数中:

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;
    // 根据对象获取对应的weak_entry_t
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        remove_referrer(entry, referrer); //将entry中的弱引用置为nil
        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);
        }
    }
    // Do not set *referrer = nil. objc_storeWeak() requires that the 
    // value not change.
}

static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
    if (! entry->out_of_line()) {
     //如果弱引用指针小于等于4,将inline_referrers中的weak指针全部置为nil
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            if (entry->inline_referrers[i] == old_referrer) {
                entry->inline_referrers[i] = nil;
                return;
            }
        }
        ......
        objc_weak_error();
        return;
    }
    // 根据对象获取对应weak_entry_t的位置
    size_t begin = w_hash_pointer(old_referrer) & (entry->mask);
    size_t index = begin;
    size_t hash_displacement = 0;
    while (entry->referrers[index] != old_referrer) {
        index = (index+1) & entry->mask;
        if (index == begin) bad_weak_table(entry);
        hash_displacement++;
        if (hash_displacement > entry->max_hash_displacement) {
            ......
            objc_weak_error();
            return;
        }
    }
    //如果弱引用指针大于4,只将referrers中对象对应的weak指针置为nil
    entry->referrers[index] = nil;
    entry->num_refs--;
}

static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
{
    // remove entry
    if (entry->out_of_line()) free(entry->referrers);
    bzero(entry, sizeof(*entry));

    weak_table->num_entries--;

    weak_compact_maybe(weak_table);
}

源码中remove_referrer函数是将inline_referrers中的所有指针置为nil或者referrers中old_referrer对应位置的weak指针置为nil,所以我推测,当对象的某个weak指针销毁了或者weak指针数量超过4个的情况,storeWeak函数的传参HaveOld才为true,如果inline_referrers有值表示weak指针数量要超过4个,则将inline_referrers内的weak指针置为nil并返回。否则去清除inline_referrers中对应的weak指针。

然后我们看新增的weak指针是如何添加的,weak_register_no_lock的源码如下:

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;

    if (!referent  ||  referent->isTaggedPointer()) return referent_id;

    // ensure that the referenced object is viable
    bool deallocating;
    if (!referent->ISA()->hasCustomRR()) {
        deallocating = referent->rootIsDeallocating();
    }
    else {
        BOOL (*allowsWeakReference)(objc_object *, SEL) = 
            (BOOL(*)(objc_object *, SEL))
            object_getMethodImplementation((id)referent, 
                                           SEL_allowsWeakReference);
        if ((IMP)allowsWeakReference == _objc_msgForward) {
            return nil;
        }
        deallocating =
            ! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
    }

     ......
    // now remember it and where it is being stored
    //添加的步骤在这里
    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        //如果原来就存在对象对应的entry,说明之前添加过weak指针,直接往后拼接
        //(referent是weak修饰的对象, referrer是weak指针,referent、referrer 傻傻分不清楚哈哈)
        append_referrer(entry, referrer);
    } 
    else {
        //原本不存在对象对应的entry,则新建一个new_entry
        weak_entry_t new_entry(referent, referrer);
        weak_grow_maybe(weak_table);
        weak_entry_insert(weak_table, &new_entry);
    }

    // Do not set *referrer. objc_storeWeak() requires that the 
    // value not change.

    return referent_id;
}

根据传进来的对象到weak_table中获取weak_entry_t,如果存在,将新增的weak指针追加到weak_entry_t 的inline_referrers或者referrers中(可查看函数append_referrer具体实现),如果不存在,则以对象和weak指针为参数新建一个weak_entry_t,并将这个weak_entry_t插入到weak_table中。

当一个对象释放,会自动调用dealloc方法,具体的调用流程如下:

weak_clear_no_lock的源码如下

void weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
{
    objc_object *referent = (objc_object *)referent_id;

    weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
    if (entry == nil) {
        /// XXX shouldn't happen, but does with mismatched CF/objc
        //printf("XXX no entry for clear deallocating %p\n", referent);
        return;
    }

    // zero out references
    weak_referrer_t *referrers;
    size_t count;
    
    if (entry->out_of_line()) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } 
    else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    
    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[i];
        if (referrer) {
            if (*referrer == referent) {
                *referrer = nil;//循环将weak指针地址置为nil
            }
            else if (*referrer) {
               ......
                objc_weak_error();
            }
        }
    }
    weak_entry_remove(weak_table, entry);
}

先跟out_of_line的值去计算weak指针的数量count,然后循环遍历指针,如果指针是指向对应对象的话,将指针置为nil。

上一篇 下一篇

猜你喜欢

热点阅读