iOS底层探索27、weak 原理分析

objc4 源码地址

一、weak_table_tweak_entry_t 的结构

源码:

/*
The weak table is a hash table governed by a single spin lock.
An allocated blob of memory, most often an object, but under GC any such 
allocation, may have its address stored in a __weak marked storage location 
through use of compiler generated write-barriers or hand coded uses of the 
register weak primitive. Associated with the registration can be a callback 
block for the case when one of the allocated chunks of memory is reclaimed. 
The table is hashed on the address of the allocated memory.  When __weak 
marked memory changes its reference, we count on the fact that we can still 
see its previous reference.

So, in the hash table, indexed by the weakly referenced item, is a list of 
all locations where this address is currently being stored.
 
For ARC, we also keep track of whether an arbitrary object is being 
deallocated by briefly placing it in the table just prior to invoking 
dealloc, and removing it via objc_clear_deallocating just prior to memory 
reclamation.

*/

// The address of a __weak variable.
// These pointers are stored disguised so memory analysis tools
// don't see lots of interior pointers from the weak table into objects.
typedef DisguisedPtr<objc_object *> weak_referrer_t;

#if __LP64__
#define PTR_MINUS_2 62
#else
#define PTR_MINUS_2 30
#endif

/**
 * The internal structure stored in the weak references table. 
 * It maintains and stores
 * a hash set of weak references pointing to an object.
 * If out_of_line_ness != REFERRERS_OUT_OF_LINE then the set
 * is instead a small inline array.
 */
#define WEAK_INLINE_COUNT 4

// out_of_line_ness field overlaps with the low two bits of inline_referrers[1].
// inline_referrers[1] is a DisguisedPtr of a pointer-aligned address.
// The low two bits of a pointer-aligned DisguisedPtr will always be 0b00
// (disguised nil or 0x80..00) or 0b11 (any other address).
// Therefore out_of_line_ness == 0b10 is used to mark the out-of-line state.
#define REFERRERS_OUT_OF_LINE 2

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

/**
 * 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;
    size_t    num_entries;
    uintptr_t mask;
    uintptr_t max_hash_displacement;
};

/// Adds an (object, weak pointer) pair to the weak table.
id weak_register_no_lock(weak_table_t *weak_table, id referent, 
                         id *referrer, bool crashIfDeallocating);

/// Removes an (object, weak pointer) pair from the weak table.
void weak_unregister_no_lock(weak_table_t *weak_table, id referent, id *referrer);

#if DEBUG
/// Returns true if an object is weakly referenced somewhere.
bool weak_is_registered_no_lock(weak_table_t *weak_table, id referent);
#endif

/// Called on object destruction. Sets all remaining weak pointers to nil.
void weak_clear_no_lock(weak_table_t *weak_table, id referent);

__END_DECLS

#endif /* _OBJC_WEAK_H_ */

下面依次开始分析 对象的weak引用释放

二、weak 原理分析

调试所用代码:

- (void)my_weak {
    
     __weak typeof(self)weakSelf = self;
    self.block = ^ {
        __strong typeof(weakSelf)strongSelf = weakSelf;
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            
            NSLog(@"%@",strongSelf.name);
        });
    };
}

1、weak引用 - 源码分析

clang 编译,报错如下:

cannot create __weak
      reference because the current deployment target does not support weak references
     __attribute__((objc_ownership(weak))) typeof(self)weakSelf = self

编译时不能创建__weak引用,不支持;__weak是运行时runtime初始化并维护的。
运行工程,通过汇编断点调试可知源码在libobjc.A.dylib.

libobjc.A.dylib`objc_initWeak:

1.1、初始化 - objc_initWeak()

objc_initWeak()源码:

/** 
 * Initialize a fresh weak pointer to some object location. 
 * It would be used for code like: 
 *
 * (The nil case) 
 * __weak id weakPtr;
 * (The non-nil case) 
 * NSObject *o = ...;
 * __weak id weakPtr = o;
 * 
 * This function IS NOT thread-safe with respect to concurrent 
 * modifications to the weak variable. (Concurrent weak clear is safe.)
 *
 * @param location Address of __weak ptr. // __weak指针的地址
 * @param newObj Object ptr. // 对象ptr
 */
id
objc_initWeak(id *location, id newObj)
{
    if (!newObj) {// 没有 objc
        *location = nil;
        return nil;
    }
    // 
    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object*)newObj);
}

DontHaveOld / DoHaveNew / DoCrashIfDeallocating

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

1.2、更新弱变量 - 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.
/**
当CrashIfDeallocating 为 true 时,如果newObj正在进行deallocating或 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)
{
    // initWeak 时:haveOld = NO, haveNew = YES, crashIfDeallocating = YES
    
    ASSERT(haveOld  ||  haveNew);
    if (!haveNew) ASSERT(newObj == nil);

    Class previouslyInitializedClass = nil;
    id oldObj;
    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:
    
    // 如果 weak指针之前弱引用过一个 obj,则将这个 obj 所对应的 SideTable 取出,赋值给 oldTable
    if (haveOld) {
        oldObj = *location;// 将旧值(__weak指针的值) 赋给 oldObj
        oldTable = &SideTables()[oldObj];// SideTablesMap.get();
    } else {
        oldTable = nil;
    }
    if (haveNew) {
        // weak指针要弱引用一个 obj,将这个 obj 所对应的 SideTable 取出,赋值给 onewTable
        newTable = &SideTables()[newObj];
    } else {
        newTable = nil;
    }

    // lockTwo 加锁,保证多线程安全
    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);

    // 有无旧值 && location与oldObj 是否一致
    // 如果 有旧值 但 location与oldObj 不同,说明当前的location已经处理过oldObj,可是又被其他线程给修改了
    if (haveOld  &&  *location != oldObj) {
        // 解锁, retry 重新处理 old
        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
        goto retry;
    }

    // 通过确保弱引用的对象没有未初始化的isa,防止 弱引用 和 初始化 之间死锁
    // 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()) 
        { // cls != nil 且 cls 未初始化
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            class_initialize(cls, (id)newObj);// 去初始化 cls

            // 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.
            // cls 赋值给 previouslyInitializedClass 重新 retry
            previouslyInitializedClass = cls;

            goto retry;
        }
    }

    // Clean up old value, if any.
    // 如果weak指针之前弱引用过别的对象oldObj,则调用weak_unregister_no_lock,在oldObj的weak_entry_t中移除该weak指针地址
    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方法,将weak指针的地址记录到newObj对应的weak_entry_t中
        // weak_entry_t 插入到 全局 weak_table 哈希表中
            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.
        // 更新newObj的isa指针的weakly_referenced bit标志位
        if (newObj  &&  !newObj->isTaggedPointer()) {
            newObj->setWeaklyReferenced_nolock();
        }

        // Do not set *location anywhere else. That would introduce a race.
        // weak指针直接指向了newObj,且没有将newObj的引用计数+1
        *location = (id)newObj;
    }
    else {
        // No new value. The storage is not changed.
    }
    
    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

    return (id)newObj;
}

第一次调用,所以是一个新的对象,也就是haveNew的情况,获取到的是新的散列表SideTable,通过weak_register_no_lock()方法来进行插入。接着来分析weak_register_no_lock函数,是怎么注册弱引用的.

1.3、注册一个新对(对象,弱指针) - weak_register_no_lock()

weak_register_no_lock() -->
注册一个新的(对象,弱指针)对。如果弱对象entry不存在,则新建一个entry:

/**
 * Registers a new (object, weak pointer) pair. Creates a new weak
 * object entry if it does not exist.
 * 
 * @param weak_table The global weak table. // 全局的弱引用表
 * @param referent The object pointed to by the weak reference. // 弱引用指向的对象
 * @param referrer The weak pointer address. // weak指针地址
 */
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;

    //如果被弱引用的对象referent为nil 或者 被弱引用对象采用了 TaggedPointer 计数方式,直接返回
    if (!referent  ||  referent->isTaggedPointer()) return referent_id;

    // ensure that the referenced object is viable
    // 确认引用对象是可用的 - 没有被析构且支持weak引用
    bool deallocating;
    if (!referent->ISA()->hasCustomRR()) {// isa 左移获取是否析构的值 has_cxx_dtor
        deallocating = referent->rootIsDeallocating();
        /**
         inline bool
         objc_object::rootIsDeallocating()
         {
             if (isTaggedPointer()) return false;
             if (isa.nonpointer) return isa.deallocating;// isa的deallocating
             return sidetable_isDeallocating();
         }
         */
    }
    else {// has_cxx_dtor = YES
        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
            return nil;
        }
    }

    // now remember it and where it is being stored
    // 记录存储
    // 在 weak_table 中找到被弱引用的对象referent所对应的 weak_entry,并将 referrer 加入到 weak_entry 中
    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        // 已有 weak_entry 则将referent追加到weak_entry
        append_referrer(entry, referrer);
    } 
    else {
        // 没有,则新建 weak_entry,并将其插入weak_table
        weak_entry_t new_entry(referent, referrer);
        weak_grow_maybe(weak_table);// 存储空间是否足够,若已超出3/4,则新开辟一个扩容到2倍的空间,并把旧空间释放free
        weak_entry_insert(weak_table, &new_entry);// 插入new_entry
    }

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

    return referent_id;
}

1.3.1、插入新对象 weak_entry_insert()

/** 
 * Add new_entry to the object's table of weak references.
 * Does not check whether the referent is already in the table.
 */
static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
{
    weak_entry_t *weak_entries = weak_table->weak_entries;
    ASSERT(weak_entries != nil);

    // 插入位置 index
    size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask);// 哈希算法根据 弱引用对象 计算出开始插入的位置 index
    size_t index = begin;
    size_t hash_displacement = 0;
    while (weak_entries[index].referent != nil) {
        index = (index+1) & weak_table->mask;
        if (index == begin) bad_weak_table(weak_entries);// 哈希冲突
        hash_displacement++;
    }
    // while 直至 referent=nil,index 则是可插入位置
    weak_entries[index] = *new_entry;
    weak_table->num_entries++;

    if (hash_displacement > weak_table->max_hash_displacement) {
        // 更新 max_hash_displacement 的值
        weak_table->max_hash_displacement = hash_displacement;
    }
}

1.3.2、拼接 append_referrer()

/** 
 * Add the given referrer to set of weak pointers in this entry.
 * Does not perform duplicate checking (b/c weak pointers are never
 * added to a set twice). 
 *
 * @param entry The entry holding the set of weak pointers. 
 * @param new_referrer The new weak pointer to be added.
 */
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
    // 判断 weak_entry 是否可使用 静态数组 inline_referrers
    if (! entry->out_of_line()) {// weak_entry的out_of_line_ness == 2
        // Try to insert inline. 尝试将 new_referrer 插入数组
        // WEAK_INLINE_COUNT = 4
        // inline_referrers[4] : inline_referrers固定的4
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            if (entry->inline_referrers[i] == nil) {
                entry->inline_referrers[i] = new_referrer;
                return;
            }
        }

        // 静态数组位置满了不能插入,则开始使用 referrers动态数组
        // Couldn't insert inline. Allocate out of line.
        // 开辟out of line空间,空间大小: 4 * weak_referrer_t
        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.
        // 这个构造的表无效,但是grow_refs_and_insert将修复它并将他重新哈希。
        // for 循环,将静态组的数据赋到新建的动态组中
        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;// num_refs 赋值 4
        entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;// 赋值 2
        entry->mask = WEAK_INLINE_COUNT-1;// mask = 3
        entry->max_hash_displacement = 0;// 最大哈希量初始化为0
    }

    ASSERT(entry->out_of_line());

    // 如果动态数组中元素个数 ≥ 数组总空间的3/4,则将数组空间扩容1倍,然后再将 new_referrer 插入数组
    if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
        return grow_refs_and_insert(entry, new_referrer);
    }
    
    // 动态数组空间足够,开始计算 new_referrer 的 index,将 new_referrer 插入 weak_entry 中
    // 计算 new_referrer 的插入位置 index
    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;// 最大哈希量 更新
    }
    // 将 new_referrer 插入 referrers 的 index 位置
    weak_referrer_t &ref = entry->referrers[index];
    ref = new_referrer;
    entry->num_refs++;// 已存数量++
}

* 弱引用对象的存储 - 总结:
针对被弱引用的对象,首先去全局弱引用表weak_tableweak_entries[] 哈希数组中寻找对应的weak_entry_t

  1. 如果weak_entry_t不存在,则会新建一个,并进行空间是否够用处理(已超出3/4则新开辟个2倍的空间并释放旧空间),然后将新建的weak_entry_t插入weak_tableweak_entries数组中对应的index(由哈希函数算出)位置;
  2. 如果weak_entry_t存在,将指向 被弱引用的对象地址的指针 referrer 通过函数append_referrer(),插入到对应的weak_entry_treferrers数组的index位置,具体处理见上面源码分析.

2、weak释放 - 源码分析

weak销毁的过程是调用析构函数dealloc,下面就来分析析构函数的原理。

- (void)dealloc {
    _objc_rootDealloc(self);
}
void
_objc_rootDealloc(id obj)
{
    ASSERT(obj);// obj 是否为空

    obj->rootDealloc();
}

objc_object::rootDealloc()

inline void
objc_object::rootDealloc()
{
    // 是 TaggedPointer 类型,直接返回
    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
        free(this);
    } 
    else {
        object_dispose((id)this);
    }
}

不能直接释放,继续 --> object_dispose():

/***********************************************************************
* object_dispose
* fixme
* Locking: none
**********************************************************************/
id 
object_dispose(id obj)
{
    if (!obj) return nil;

    objc_destructInstance(obj);    
    free(obj);

    return nil;
}

2.1、首先销毁 C++析构函数关联对象

objc_destructInstance():

/***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory. 
* Calls C++ destructors.
* Calls ARC ivar cleanup.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is 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.
        // 如果有C++的析构函数,则将C++析构函数从类中销毁
        if (cxx) object_cxxDestruct(obj);
        // 有关联对象,将关联对象全部移除,并将自己也从 assocationManager的map中移除
        if (assoc) _object_remove_assocations(obj);
        
        // 继续 dealloc 处理对象的其他引用  
        obj->clearDeallocating();
    }

    return obj;
}

2.2、销毁其他

objc_object::clearDeallocating()

inline void 
objc_object::clearDeallocating()
{
    // 1. 要释放的对象没有采用优化过的isa引用计数
    if (slowpath(!isa.nonpointer)) {
        // Slow path for raw pointer isa.
        sidetable_clearDeallocating();
    }
    // 2. 要释放的对象采用优化过的isa引用计数 且 有弱引用 或 有用sidetable来引用计数
    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());
}

2.2.1、对象没有采用优化过的isa引用计数

sidetable_clearDeallocating():

void 
objc_object::sidetable_clearDeallocating()
{
    // 获取当前对象的 SideTable
    SideTable& table = SideTables()[this];

    // clear any weak table items
    // clear extra retain count and deallocating bit
    // (fixme warn or abort if extra retain count == 0 ?)
    table.lock();
    // 在散列表中找到对应的引用计数表 RefcountMap,拿到要释放的对象的引用计数
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it != table.refcnts.end()) {
        if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
            // 从 weak_table 中,找到要释放对象的 entry,对entry中referrers遍历将数组中的弱引用 全部置为 nil
            weak_clear_no_lock(&table.weak_table, (id)this);
        }
        // 从引用计数表中 擦除当前对象的引用计数
        table.refcnts.erase(it);
    }
    table.unlock();
}

2.2.2、对象采用了优化过的isa引用计数

objc_object::clearDeallocating_slow():

// Slow path of clearDeallocating() 
// for objects with nonpointer isa
// that were ever weakly referenced 
// or whose retain count ever overflowed to the side table.
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
    ASSERT(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));

    // 获取当前对象散列表 sideTable
    SideTable& table = SideTables()[this];
    table.lock();
    if (isa.weakly_referenced) {
        // 有弱引用,将全部弱引用置为 nil
        weak_clear_no_lock(&table.weak_table, (id)this);
    }
    if (isa.has_sidetable_rc) {
        // 使用了sideTable的辅助引用计数,直接在SideTable中擦除该对象的引用计数
        table.refcnts.erase(this);
    }
    table.unlock();
}

弱引用了对象的 weak指针置为 nil

weak_clear_no_lock(&table.weak_table, (id)this):

/** 
 * Called by dealloc; nils out all weak pointers that point to the 
 * provided object so that they can no longer be used.
 * 
 * @param weak_table 
 * @param referent The object being deallocated. 
 */
void 
weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
{
    // 当前被引用对象
    objc_object *referent = (objc_object *)referent_id;

    // 从 weak_table 中获取 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;
    
    // 弱引用该对象的 所有weak指针地址 的数组 referrers
    // referrers 数组的 count
    if (entry->out_of_line()) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } 
    else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    
    // 遍历,将referrers数组内的 weak指针 全部置为 nil
    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[i];
        if (referrer) {
            
            // 如果weak指针 弱引用了对象 referent,则将weak指针设置为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);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容