面试题:Weak的原理

425 阅读2分钟

1.OC中所有的对象都有一个isa指针,这个指针在arm64优化后,使用共生体存储着是否有weak引用信息,销毁的时候会先检查该类是否有weak引用。

image.png

2.OC的对象引用计数都存储在SideTable里,SideTables是一个64个元素长度8个元素长度的hash数组,里面存储了SideTable。

SideTables的hash键值就是一个对象obj的地址。一个obj,对应了一个SideTable。但是一个SideTable,会对应多个obj。因为SideTable的数量只有64个,所以会有很多obj共用同一个SideTable。

struct SideTable {
    spinlock_t slock;       // 自旋锁
    RefcountMap refcnts;    // 强引用哈希表
    weak_table_t weak_table;  // 弱引用表
};

/**
全局hash表,以对象指针地址为key,entries里面存储着当前对象的所有弱引用指针
*/
struct weak_table_t {
    weak_entry_t *weak_entries;     // 保存了对象的所有weak指针
    size_t    num_entries;          // weak对象的存储空间
    uintptr_t mask;                 // 参与判断引用计数辅助量
    uintptr_t max_hash_displacement;// 最大偏移量
};

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];
        };
    };
};
  1. weak引用是通过runtime来支持的,runtime会调用方法objc_initweak,这个方法接受两个参数,一个引用对象的指针地址,用于在最后将weak指针指向对象置为nil。

  2. objc_initweak实质上调用了storeWeak方法,storeWeak() 的作用是更新指针指向,创建对应的弱引用表。

__weak id weakObj = obj;
// 本质上会调用objc_initWeak函数
id
objc_initWeak(id *location, id newObj)
{
    // 1.判断newObj是否为nil,如果是nil则将当前的指针置为nil
    if (!newObj) {
        *location = nil;
        return nil;
    }
    // 2. 如果传入的指针不为nil,则会调用storeWeak。
    //  storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object*)newObj);
}
  1. weak对象释放时,底层会调用_objc_rootDealloc的rootDealloc方法,查看isa中的是否有若引用,没有会直接释放。如果有则会调用object_dispose()方法,然后做清除成员变量、移除关联对象操作,把key从weaktable中移除,最后将当前对象的弱指针置为nil。
// 1. 会先调用_objc_rootDealloc
void
_objc_rootDealloc(id obj)
{
    assert(obj);

    obj->rootDealloc();
}
// 2. 看是否能直接释放
inline void
objc_object::rootDealloc()
{
    if (isTaggedPointer()) return;  // fixme necessary?

    if (fastpath(isa.nonpointer  &&          // 如果是nonpointer(优化过的isa指针)
                 !isa.weakly_referenced  &&  // 没有弱引用
                 !isa.has_assoc  &&          // 没有关联对象
                 !isa.has_cxx_dtor  &&       // 没有c++析构函数
                 !isa.has_sidetable_rc))     // 没有使用sidetable存储引用计数
    {
        assert(!sidetable_present());
        free(this); // 直接释放对象
    } 
    else {
        object_dispose((id)this);
    }
}
// 3. 如果不能直接释放则调用
id 
object_dispose(id obj)
{
    if (!obj) return nil;

    objc_destructInstance(obj);    
    free(obj);

    return nil;
}
// 4. c++和关联对象释放以及成功变量擦除
void *objc_destructInstance(id obj) 
{
    if (obj) {
        // Read all of the flags at once for performance.
        bool cxx = obj->hasCxxDtor();
        bool assoc = obj->hasAssociatedObjects();

        // This order is important.
        if (cxx) object_cxxDestruct(obj); // 清除成员变量
        if (assoc) _object_remove_assocations(obj); // 移除关联对象
        obj->clearDeallocating(); // 将指向当前对象的弱指针置为nil
    }

    return obj;
}

inline void 
objc_object::clearDeallocating()
{
    if (slowpath(!isa.nonpointer)) {
        // Slow path for raw pointer isa.
        sidetable_clearDeallocating();
    }
    else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
        // Slow path for non-pointer isa with weak refs and/or side table data.
        clearDeallocating_slow();
    }

    assert(!sidetable_present());
}