weak原理

weak原理

弱引用指针添加到弱引用表。

NSObject.mm

id
objc_initWeak(id *location, id newObj)//location是当前对象的地址    newObj是要weak的对象
{
    if (!newObj) {
        *location = nil;
        return nil;
    }
    //C++模版函数  第一次进来DontHaveOld是false  DoHaveNew是true
    return storeWeak
        (location, (objc_object*)newObj);
}
static id 
storeWeak(id *location, objc_object *newObj)
{
    ASSERT(haveOld  ||  haveNew);
    if (!haveNew) ASSERT(newObj == nil);

    Class previouslyInitializedClass = nil;
    id oldObj;
    //SideTable用来管理引用计数和弱引用表
    SideTable *oldTable;
    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:
    if (haveOld) {
        oldObj = *location;
        oldTable = &SideTables()[oldObj];
    } else {
        oldTable = nil;
    }
    if (haveNew) {
        newTable = &SideTables()[newObj];
    } else {
        newTable = nil;
    }

    SideTable::lockTwo(oldTable, newTable);

    if (haveOld  &&  *location != oldObj) {
        SideTable::unlockTwo(oldTable, newTable);
        goto retry;
    }

    // Prevent a deadlock between the weak reference machinery
    // and the +initialize machinery by ensuring that no 
    // weakly-referenced object has an un-+initialized isa.
      //保证对象非空 并且已经初始化的
    if (haveNew  &&  newObj) {
        Class cls = newObj->getIsa();
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized()) 
        {
            SideTable::unlockTwo(oldTable, newTable);
            class_initialize(cls, (id)newObj);

            // If this class is finished with +initialize then we're good.
            // If this class is still running +initialize on this thread 
            // (i.e. +initialize called storeWeak on an instance of itself)
            // then we may proceed but it will appear initializing and 
            // not yet initialized to the check above.
            // Instead set previouslyInitializedClass to recognize it on retry.
            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(oldTable, newTable);

    return (id)newObj;
}
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
    static void lockTwo(SideTable *lock1, SideTable *lock2);
    template
    static void unlockTwo(SideTable *lock1, SideTable *lock2);
};

弱引用的指针存储到弱引用表

通过哈希运算找到弱引用表的地址,然后把弱引用指针插入到弱引用表。

总结:

当前runtime维护了一张全局的弱引用表,也是哈希表,全局的弱引用表保存了所有的弱引用对象。

weak表(弱引用表):

key:当前对象地址(因为一个对象在内存中的地址是不变的)

value:weak指针的地址,是一个数组 存储所有和相关对象的弱引用指针。weak指针的地址指向当前对象的地址。

调用流程:

  • objc_initWeak

    objc_initWeak调用storeWeak存储weak

  • store_weak

    storeWeak方法中SideTable用来管理引用计数和弱引用表,根据当前对象的指针通过哈希运算把当前对象的SideTable取出来。

    store_weak中调用weak_register,weak_unregister

    • weak_register_no_lock

      弱引用指针存储到弱引用表。通过哈希运算,放入weak_table

      weak_register_no_lock参数是:当前对象的弱引用表,当前对象,地址指针。

      weak_register_no_lock方法里面调用weak_entry_for_referent,把当前要弱引用的对象添加到弱引用表。

      • weak_entry_for_referent

        weak_entry_for_referent方法里面通过哈希运算找到当前弱引用表的地址,然后插入。

    • setWeaklyReferenced_nolock

      store_weak中执行完weak_register_no_lock之后,又调用了setWeaklyReferenced_nolock,把当前对象的weakly_referenced置为true,表明当前对象是一个弱引用对象。

  • objc_destroyWeak

声明weak要不断的通过hash计算来找到地址然后取出表,来进行查找。

声明太多的weak比较耗费性能。只在解决循环引用的时候使用。

你可能感兴趣的:(weak原理)