iOS-weak的底层实现

1,171 阅读11分钟

weak关键字

weakios开发过程中一个非常常见的关键字,使用场景有以下几个:

1. 声明弱引用属性
2. 使用__weak来创建一个弱引用指针

weak的主要作用就是用于内存管理,一个weak类型指针wptr指向的object被释放之后,系统会自动将wptr置为nil,防止其他代码访问到wptr造成野指针异常。今天主要就跟随源码来探索一下系统是如何实现这种机制的。

代码示例

代码如下:

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        DemoObject *object1 = [DemoObject new];
        DemoObject *object2 = [DemoObject new];

        __weak id wObject = object1;
        wObject = object2;
    }
    return 0;
}

主要调用流程以及对应关系如下:

  1. objc_initWeak ---> __weak id wObject = object1
  2. objc_storeWeak ---> wObject = object2
  3. objc_destroyWeak ---> 出了作用域之后wObject指针需要被回收
  4. objc_storeStrong ---> 出了作用域之后object1、object2指针需要被回收,调用了2次

源码跟踪

接下来我们跟随libobjc的源码来探索weak的内部实现。 不过在此之前,我们需要来了解几个结构,以便于更好的阅读源码

几个结构

StripedMap

// RefcountMap disguises its pointers because we 
// don't want the table to act as a root for `leaks`.
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;

// StripedMap<T> is a map of void* -> T, sized appropriately 
// for cache-friendly lock striping. 
// For example, this may be used as StripedMap<spinlock_t>
// or as StripedMap<SomeStruct> where SomeStruct stores a spin lock.

template<typename T> 
class StripedMap {
    #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
        enum { StripeCount = 8 };
    #else
        enum { StripeCount = 64 };
    #endif
    ...
    struct PaddedT {
        T value alignas(CacheLineSize);
    };
    
    PaddedT array[StripeCount];
	// 哈希函数
    static unsigned int indexForPointer(const void *p) {
        uintptr_t addr = reinterpret_cast<uintptr_t>(p);
        return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
    }
    // 运算符重载取值
    T& operator[] (const void *p) { 
        return array[indexForPointer(p)].value; 
    }
    ...
}

StripedMap是一个哈希表,包含了一个在ios下容量为8的数组,数组每一个元素是结构体PaddedT,内部包含了一个T类型的value变量。在本例中可以简单的理解为一个存储SideTable的哈希表,容量为8

SideTable

// RefcountMap disguises its pointers because we 
// don't want the table to act as a root for `leaks`.
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;

// Template parameters.
enum HaveOld { DontHaveOld = false, DoHaveOld = true };
enum HaveNew { DontHaveNew = false, DoHaveNew = true };

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

    ...
};

SideTable有三个变量:

  • 互斥锁 slock
  • 引用计数表 refcnts
  • 弱引用表 weak_table

weak_table_t

/**
 * The global weak references table. Stores object ids as keys,
 * and weak_entry_t structs as their values.
 */
struct weak_table_t {
    weak_entry_t *weak_entries; // entry数组
    size_t    num_entries; // 当前的entry个数
    uintptr_t mask; // 容量-1
    uintptr_t max_hash_displacement;
};

weak_entry_t

typedef DisguisedPtr<objc_object *> weak_referrer_t; // 可以理解为是一个对象指针,进行伪装的目的是为了避免被内存检测工具误认为出现内存泄漏

#if __LP64__
#define PTR_MINUS_2 62 // 共用体变量num_refs的位数
#else
#define PTR_MINUS_2 30
#endif

#define WEAK_INLINE_COUNT 4 // inline_referrers数组的容量
#define REFERRERS_OUT_OF_LINE 2 // 标识是用数组还是referrers存储弱引用指针

struct weak_entry_t {
    DisguisedPtr<objc_object> referent; // 弱引用对象obj
    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;
    }
    // entry的初始化方法,默认利用inline_referrers数组存储指针地址
    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;
        }
    }
};

这几个结构之间的关联关系如下图: 大致了解了上面几个结构体之后,我们可以继续下面的步骤,在上面图中断点中直接点击Step into会直接进入objc_initWeak函数。

objc_initWeak

// Initialize a fresh weak pointer to some object location. 
id
objc_initWeak(id *location, id newObj)
{
    if (!newObj) {
        *location = nil;
        return nil;
    }

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

对应的是__weak id wObject = object1

注意此时storeWeak的模板参数为:

  • DontHaveOld 没有旧值
  • DoHaveNew 有新值

storeWeak先略过,我们先看objc_storeWeak函数

objc_storeWeak

// This function stores a new value into a __weak variable. It would
 * be used anywhere a __weak variable is the target of an assignment.
id
objc_storeWeak(id *location, id newObj)
{
    return storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object *)newObj);
}

对应的是wObject = object2;

注意此时storeWeak的模板参数为:

  • DoHaveOld 有旧值
  • DoHaveNew 有新值

从上述代码可以看出init/store方法一个是在初始化时调用,一个是重新赋值时调用,最终都调用了storeWeak函数。很显然,storeWeak函数是我们研究的重点。

storeWeak

// Update a weak variable. 
// If HaveOld is true, the variable has an existing value 
//   that needs to be cleaned up. This value might be nil.
// If HaveNew is true, there is a new value that needs to be 
//   assigned into the variable. This value might be nil.
// If CrashIfDeallocating is true, the process is halted if newObj is 
//   deallocating or newObj's class does not support weak references. 
//   If CrashIfDeallocating is false, nil is stored instead.
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; // weak指针指向的旧值
    SideTable *oldTable; // 旧值对应的sidetable
    SideTable *newTable; // 新值对应的sidetable

    // 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; // 得到旧值和sidetable
        oldTable = &SideTables()[oldObj];
    } 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;
    }

    // Prevent a deadlock between the weak reference machinery
    // and the +initialize machinery by ensuring that no 
    // weakly-referenced object has an un-+initialized isa.
    // 此处的逻辑是为了防止在initialize方法中出现相关弱引用的逻辑,此时initialize还没有执行完会出现递归调用,通过previouslyInitializedClass来打破递归
    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);

            // 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_table中的信息
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }

    // Assign new value, if any.
    if (haveNew) {
    	// 在新值所对应的weak_table中的注册当前弱引用指针的信息
        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()) {
            // 更新object的isa中的相关标识,标记存在弱引用
            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;
}

storeWeak代码比较多,大致分为一下三步:

  1. 从哈希表中获取新值和旧值对应的sidetable
  2. weak_unregister_no_lock清理旧值在sidetable中对应的信息
  3. weak_register_no_lock在新值对应的sidetable中注册对应的信息,同时更新新值的isa

weak_unregister_no_lock 解注册weak指针

void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, 
                        id *referrer_id)
{
    // 对应object
    objc_object *referent = (objc_object *)referent_id;
    // 指向weak指针的指针
    objc_object **referrer = (objc_object **)referrer_id;

    weak_entry_t *entry;

    if (!referent) return;
	// 在weak_table中寻找object对应的entry
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
    	// 移除对应的entry里面的指针,只是解除了weak指针和当前object的关系,后面weak指针有可能会指向其他的对象。
        remove_referrer(entry, referrer);
        bool empty = true;
        // 判断entry是否为空,entry有两种存储方式,因此需要区分对待
        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;
                }
            }
        }
	// entry为空时需要从weak_table中移除entry
        if (empty) {
            weak_entry_remove(weak_table, entry);
        }
    }

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

梳理了逻辑之后,流程就比较清晰了:

  1. weak_table中寻找referent_id(object)对应的entry
  2. entry中移除referrer(指向weak的指针)
  3. entry为空时处理移除entry的逻辑

weak_register_no_lock 注册weak指针

id 
weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
                      id *referrer_id, bool crashIfDeallocating)
{
    //  object
    objc_object *referent = (objc_object *)referent_id;
    // weak指针的地址
    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, 
                                           @selector(allowsWeakReference));
        if ((IMP)allowsWeakReference == _objc_msgForward) {
            return nil;
        }
        deallocating =
            ! (*allowsWeakReference)(referent, @selector(allowsWeakReference));
    }

    if (deallocating) {
        if (crashIfDeallocating) {
            _objc_fatal("Cannot form weak reference to instance (%p) of "
                        "class %s. It is possible that this object was "
                        "over-released, or is in the process of deallocation.",
                        (void*)referent, object_getClassName((id)referent));
        } else {
            return nil;
        }
    }

    // now remember it and where it is being stored
    weak_entry_t *entry; /**找到obj对应的weak_entry_t*/
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
    	// 将地址插入到entry的数组中,其中会涉及到entry内部结构的变化
        append_referrer(entry, referrer);
    } 
    else {
        // 没有找到entry,需要新建一个entry
        weak_entry_t new_entry(referent, referrer); /** (对象, 指针)   
        // 加入之后weak_table可能发生容量的变化
        weak_grow_maybe(weak_table);
        // entry插入到weak_table中
        weak_entry_insert(weak_table, &new_entry);
    }

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

    return referent_id;
}

和解注册类似,需要通过object找到对应的entry之后,将弱引用指针地址存储对应的entry中,如果没有找到entry还需要创建新的entry并插入到weak_table中。

objc_destroyWeak

前面的代码中,当作用域结束时,需要解除weak指针和指向对象的关联关系,调用的就是objc_destoryWeak方法。

void
objc_destroyWeak(id *location)
{
    (void)storeWeak<DoHaveOld, DontHaveNew, DontCrashIfDeallocating>
        (location, nil);
}

本质也是调用了storeWeak函数,只不过传入的参数为DoHaveOld, DontHaveNew,因此执行了解注册的操作。

weak指针自动置nil的原理

因为weak并不会增加object的引用计数,因此在weak指针指向的情况下,object也会被释放,当weak指针指向的object被释放之后,系统会自动将weak指针指向nil,防止出现野指针异常,我们从dealloc方法入手,来看一下其原理

release

dealloc打下断点,查看调用堆栈,可以发现当我们的object1出了作用域后,系统会调用objc_storeStrong(id *location, id obj)的方法 其中的location记录了object1指针的地址,obj的值为nil,而prev为全局变量globalObject的地址。在这种情境下,objc_storeStrong主要就是触发了objc_releaseobject1release操作。

顺带讲一下后续的流程,仅针对当前情况:

  1. 通过object1isa_t,对其中的extra_rc即引用计数进行--操作
  2. extra_rc溢出的时候会判断isa_t中的has_sidetable_rc标识,发现并没有使用sidetable来存储额外的引用计数。
  3. 此时进入object1dealloc流程,通过消息发送执行dealloc流程

dealloc

dealloc函数的反汇编实现如下图: 可以发现在 dealloc的最后调用了super的实现。

- (void)dealloc {
    _objc_rootDealloc(self);
}

void
_objc_rootDealloc(id obj)
{
    ASSERT(obj);

    obj->rootDealloc();
}

inline void
objc_object::rootDealloc()
{
    if (isTaggedPointer()) return;  // fixme necessary?

    if (fastpath(isa.nonpointer  &&  
                 !isa.weakly_referenced  &&  
                 !isa.has_assoc  &&  
                 !isa.has_cxx_dtor  &&  
                 !isa.has_sidetable_rc))
    {
        assert(!sidetable_present());
        free(this);
    } 
    else {
        object_dispose((id)this);
    }
}

可以看到在rootDealloc方法中进行了一次判断:

* 不是nonpointer
* weakly_referenced:弱引用
* has_assoc:关联对象
* has_cxx_dtor: cxx析构函数
* has_sidetable_rc: 使用sidetable存储了额外的引用计数

如果上述五个条件有任何一个命中,系统会执行object_dispose函数,否则会直接释放当前对象的内存空间。这五个条件中,其中有一个就是弱引用的标识weakly_referenced

id 
object_dispose(id obj)
{
    if (!obj) return nil;

    objc_destructInstance(obj);    
    free(obj);

    return nil;
}

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); // 对自己的属性进行release操作
        if (assoc) _object_remove_assocations(obj); // 移除关联对象
        obj->clearDeallocating();
    }

    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.
        // 处理有弱引用或者使用sidetable存储引用计数的non-pointer isa
        clearDeallocating_slow();
    }

    assert(!sidetable_present());
}

NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
    ASSERT(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));

    SideTable& table = SideTables()[this];
    table.lock();
    if (isa.weakly_referenced) {
    	// 清理弱引用表
        weak_clear_no_lock(&table.weak_table, (id)this);
    }
    if (isa.has_sidetable_rc) {
    	// 抹去弱引用表中关于当前对象的引用计数
        table.refcnts.erase(this);
    }
    table.unlock();
}

weak_clear_no_lock

经历了漫长的流程来到了和weak指针相关的部分。

void 
weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
{
    objc_object *referent = (objc_object *)referent_id;
    // 找到对应的entry
    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()) {
    	// 如果是以非inline的方式,count = mask+1
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } 
    else {
    	// inline的方式count = 4
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    
    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[i];
        if (referrer) {
            // 核心代码:弱引用指针置nil
            if (*referrer == referent) {
                *referrer = nil;
            }
            else if (*referrer) {
                _objc_inform("__weak variable at %p holds %p instead of %p. "
                             "This is probably incorrect use of "
                             "objc_storeWeak() and objc_loadWeak(). "
                             "Break on objc_weak_error to debug.\n", 
                             referrer, (void*)*referrer, (void*)referent);
                objc_weak_error();
            }
        }
    }
    // 移除weak_table中对应的entry
    weak_entry_remove(weak_table, entry);
}

到此为止,解释了weak指针在指向的对象释放时自动置nil的机制。

关于weak_entry_t和weak_table的一些操作的具体实现

weak_entry_for_referent

weak_table中查找object对应的entry

static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
    ASSERT(referent);
    weak_entry_t *weak_entries = weak_table->weak_entries;

    if (!weak_entries) return nil;
    // 通过哈希函数得到object在weak_entries中的位置
    size_t begin = hash_pointer(referent) & weak_table->mask;
    size_t index = begin;
    size_t hash_displacement = 0;
    // 从得到的位置开始遍历哈希表,查找对应的entry
    while (weak_table->weak_entries[index].referent != referent) {
        index = (index+1) & weak_table->mask;
        if (index == begin) bad_weak_table(weak_table->weak_entries);
        hash_displacement++;
        if (hash_displacement > weak_table->max_hash_displacement) {
            return nil;
        }
    }
    
    return &weak_table->weak_entries[index];
}

append_referrer

将指针地址放入对应的entry中

static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
    if (! entry->out_of_line()) {
        // Try to insert inline.
        // inline的情况下遍历inline数组,如果有空位置就插入
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            if (entry->inline_referrers[i] == nil) {
                entry->inline_referrers[i] = new_referrer;
                return;
            }
        }
	// inline已经满了,需要开辟新的空间
        // 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.
        // 将inline的数组转移到重新开辟的new_referrers中, 这一次的操作是无效的,因为后面会对new_referrers进行扩容。。。
        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;
    }

    ASSERT(entry->out_of_line());
	// 存储的entry超过了容量的3/4,需要进行扩容并插入弱引用指针的地址
    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++;
}

grow_refs_and_insert

entry扩容并且插入弱引用指针的地址

__attribute__((noinline, used))
static void grow_refs_and_insert(weak_entry_t *entry, 
                                 objc_object **new_referrer)
{
    ASSERT(entry->out_of_line());

    size_t old_size = TABLE_SIZE(entry);
    // 容量翻倍
    size_t new_size = old_size ? old_size * 2 : 8;

    size_t num_refs = entry->num_refs;
    weak_referrer_t *old_refs = entry->referrers;
    entry->mask = new_size - 1;
    
    entry->referrers = (weak_referrer_t *)
        calloc(TABLE_SIZE(entry), sizeof(weak_referrer_t));
    entry->num_refs = 0;
    entry->max_hash_displacement = 0;
    // 旧的entry中的地址加入到新开辟的entry中
    for (size_t i = 0; i < old_size && num_refs > 0; i++) {
        if (old_refs[i] != nil) {
            append_referrer(entry, old_refs[i]);
            num_refs--;
        }
    }
    // Insert
    // 插入新的地址
    append_referrer(entry, new_referrer);
    // 释放旧的entry
    if (old_refs) free(old_refs);
}

remove_referrer

static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
    if (! entry->out_of_line()) {
    	// inline的情况下只需要将对应的地址移除掉
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            if (entry->inline_referrers[i] == old_referrer) {
                entry->inline_referrers[i] = nil;
                return;
            }
        }
        _objc_inform("Attempted to unregister unknown __weak variable "
                     "at %p. This is probably incorrect use of "
                     "objc_storeWeak() and objc_loadWeak(). "
                     "Break on objc_weak_error to debug.\n", 
                     old_referrer);
        objc_weak_error();
        return;
    }

    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++;
        // 超过了最大的步数,表示出了异常,因为当前的max_hash_displacement记录的是最大的步数,这应该属于一种优化。
        if (hash_displacement > entry->max_hash_displacement) {
            _objc_inform("Attempted to unregister unknown __weak variable "
                         "at %p. This is probably incorrect use of "
                         "objc_storeWeak() and objc_loadWeak(). "
                         "Break on objc_weak_error to debug.\n", 
                         old_referrer);
            objc_weak_error();
            return;
        }
    }
    // 将对应的地址指向nil,可能是因为超出了作用域此时的指针被栈回收了,因此需要置为nil,否则会出现野指针异常
    entry->referrers[index] = nil;
    entry->num_refs--;
}

weak_entry_remove

移除entry

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

weak_compact_maybe

weak_table的size太大,但是内部的entry个数太小时,需要缩减容量

// Shrink the table if it is mostly empty.
static void weak_compact_maybe(weak_table_t *weak_table)
{
    size_t old_size = TABLE_SIZE(weak_table);

    // Shrink if larger than 1024 buckets and at most 1/16 full.
    if (old_size >= 1024  && old_size / 16 >= weak_table->num_entries) {
        weak_resize(weak_table, old_size / 8);
        // leaves new table no more than 1/2 full
    }
}

weak_grow_maybe

检测weak_table是否需要扩容

static void weak_grow_maybe(weak_table_t *weak_table)
{
    size_t old_size = TABLE_SIZE(weak_table);/**根据 mask来获取原有weak_table的容量*/

    // Grow if at least 3/4 full.
    if (weak_table->num_entries >= old_size * 3 / 4) {
        weak_resize(weak_table, old_size ? old_size*2 : 64);
    }
}

weak_resize

static void weak_resize(weak_table_t *weak_table, size_t new_size)
{
    size_t old_size = TABLE_SIZE(weak_table);

    weak_entry_t *old_entries = weak_table->weak_entries;
    weak_entry_t *new_entries = (weak_entry_t *)
        calloc(new_size, sizeof(weak_entry_t));

    weak_table->mask = new_size - 1;
    weak_table->weak_entries = new_entries;
    weak_table->max_hash_displacement = 0;
    weak_table->num_entries = 0;  // restored by weak_entry_insert below
    
    if (old_entries) {
        weak_entry_t *entry;
        // 得到原有entry的终点
        weak_entry_t *end = old_entries + old_size;
        // 遍历整个entry,插入到新开辟的空间中
        for (entry = old_entries; entry < end; entry++) {
            if (entry->referent) {
                weak_entry_insert(weak_table, entry);
            }
        }
        free(old_entries);
    }
}