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

objc4 源碼地址

一节仿、weak_table_tweak_entry_t 的結(jié)構(gòu)

源碼:

/*
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_ */

下面依次開始分析 對(duì)象的weak引用釋放遇绞。

二除呵、weak 原理分析

調(diào)試所用代碼:

- (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 編譯轧坎,報(bào)錯(cuò)如下:

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

編譯時(shí)不能創(chuàng)建__weak引用宏邮,不支持;__weak是運(yùn)行時(shí)runtime初始化并維護(hù)的缸血。
運(yùn)行工程蜜氨,通過匯編斷點(diǎn)調(diào)試可知源碼在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. // 對(duì)象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.
/**
當(dāng)CrashIfDeallocating 為 true 時(shí)飒炎,如果newObj正在進(jìn)行deallocating或 newObj的類不支持弱引用,進(jìn)程將被停止笆豁;
當(dāng)CrashIfDeallocating 為 false 時(shí)郎汪,則存儲(chǔ) nil.
*/
enum CrashIfDeallocating {
    DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};
template <HaveOld haveOld, HaveNew haveNew,
          CrashIfDeallocating crashIfDeallocating>
static id 
storeWeak(id *location, objc_object *newObj)
{
    // initWeak 時(shí):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指針之前弱引用過一個(gè) obj,則將這個(gè) obj 所對(duì)應(yīng)的 SideTable 取出闯狱,賦值給 oldTable
    if (haveOld) {
        oldObj = *location;// 將舊值(__weak指針的值) 賦給 oldObj
        oldTable = &SideTables()[oldObj];// SideTablesMap.get();
    } else {
        oldTable = nil;
    }
    if (haveNew) {
        // weak指針要弱引用一個(gè) obj煞赢,將這個(gè) obj 所對(duì)應(yīng)的 SideTable 取出,賦值給 onewTable
        newTable = &SideTables()[newObj];
    } else {
        newTable = nil;
    }

    // lockTwo 加鎖哄孤,保證多線程安全
    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);

    // 有無舊值 && location與oldObj 是否一致
    // 如果 有舊值 但 location與oldObj 不同照筑,說明當(dāng)前的location已經(jīng)處理過oldObj,可是又被其他線程給修改了
    if (haveOld  &&  *location != oldObj) {
        // 解鎖瘦陈, retry 重新處理 old
        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
        goto retry;
    }

    // 通過確保弱引用的對(duì)象沒有未初始化的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指針之前弱引用過別的對(duì)象oldObj,則調(diào)用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 *)
        // 調(diào)用weak_register_no_lock方法蛾默,將weak指針的地址記錄到newObj對(duì)應(yīng)的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標(biāo)志位
        if (newObj  &&  !newObj->isTaggedPointer()) {
            newObj->setWeaklyReferenced_nolock();
        }

        // Do not set *location anywhere else. That would introduce a race.
        // weak指針直接指向了newObj,且沒有將newObj的引用計(jì)數(shù)+1
        *location = (id)newObj;
    }
    else {
        // No new value. The storage is not changed.
    }
    
    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

    return (id)newObj;
}

第一次調(diào)用咏花,所以是一個(gè)新的對(duì)象趴生,也就是haveNew的情況,獲取到的是新的散列表SideTable昏翰,通過weak_register_no_lock()方法來進(jìn)行插入苍匆。接著來分析weak_register_no_lock函數(shù),是怎么注冊(cè)弱引用的.

1.3棚菊、注冊(cè)一個(gè)新對(duì)(對(duì)象浸踩,弱指針) - weak_register_no_lock()

weak_register_no_lock() -->
注冊(cè)一個(gè)新的(對(duì)象,弱指針)對(duì)统求。如果弱對(duì)象entry不存在检碗,則新建一個(gè)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. // 弱引用指向的對(duì)象
 * @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;

    //如果被弱引用的對(duì)象referent為nil 或者 被弱引用對(duì)象采用了 TaggedPointer 計(jì)數(shù)方式据块,直接返回
    if (!referent  ||  referent->isTaggedPointer()) return referent_id;

    // ensure that the referenced object is viable
    // 確認(rèn)引用對(duì)象是可用的 - 沒有被析構(gòu)且支持weak引用
    bool deallocating;
    if (!referent->ISA()->hasCustomRR()) {// isa 左移獲取是否析構(gòu)的值 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));
    }

    // 正在析構(gòu),則不能被弱引用
    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
    // 記錄存儲(chǔ)
    // 在 weak_table 中找到被弱引用的對(duì)象referent所對(duì)應(yīng)的 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);// 存儲(chǔ)空間是否足夠怕犁,若已超出3/4边篮,則新開辟一個(gè)擴(kuò)容到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奏甫、插入新對(duì)象 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);// 哈希算法根據(jù) 弱引用對(duì)象 計(jì)算出開始插入的位置 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 是否可使用 靜態(tài)數(shù)組 inline_referrers
    if (! entry->out_of_line()) {// weak_entry的out_of_line_ness == 2
        // Try to insert inline. 嘗試將 new_referrer 插入數(shù)組
        // 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;
            }
        }

        // 靜態(tài)數(shù)組位置滿了不能插入阵子,則開始使用 referrers動(dòng)態(tài)數(shù)組
        // 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.
        // 這個(gè)構(gòu)造的表無效,但是grow_refs_and_insert將修復(fù)它并將他重新哈希挠进。
        // for 循環(huán)色乾,將靜態(tài)組的數(shù)據(jù)賦到新建的動(dòng)態(tài)組中
        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());

    // 如果動(dòng)態(tài)數(shù)組中元素個(gè)數(shù) ≥ 數(shù)組總空間的3/4,則將數(shù)組空間擴(kuò)容1倍奈梳,然后再將 new_referrer 插入數(shù)組
    if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
        return grow_refs_and_insert(entry, new_referrer);
    }
    
    // 動(dòng)態(tài)數(shù)組空間足夠杈湾,開始計(jì)算 new_referrer 的 index,將 new_referrer 插入 weak_entry 中
    // 計(jì)算 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++;// 已存數(shù)量++
}

* 弱引用對(duì)象的存儲(chǔ) - 總結(jié):
針對(duì)被弱引用的對(duì)象攘须,首先去全局弱引用表weak_tableweak_entries[] 哈希數(shù)組中尋找對(duì)應(yīng)的weak_entry_t漆撞,

  1. 如果weak_entry_t不存在,則會(huì)新建一個(gè)于宙,并進(jìn)行空間是否夠用處理(已超出3/4則新開辟個(gè)2倍的空間并釋放舊空間)浮驳,然后將新建的weak_entry_t插入weak_tableweak_entries數(shù)組中對(duì)應(yīng)的index(由哈希函數(shù)算出)位置捞魁;
  2. 如果weak_entry_t存在至会,將指向 被弱引用的對(duì)象地址的指針 referrer 通過函數(shù)append_referrer(),插入到對(duì)應(yīng)的weak_entry_treferrers數(shù)組的index位置谱俭,具體處理見上面源碼分析.

2奉件、weak釋放 - 源碼分析

weak銷毀的過程是調(diào)用析構(gòu)函數(shù)dealloc,下面就來分析析構(gòu)函數(shù)的原理昆著。

- (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 開啟了優(yōu)化的 isa 計(jì)數(shù)方式
                 !isa.weakly_referenced  && // 對(duì)象有無被弱引用
                 !isa.has_assoc  &&         // 對(duì)象有沒有關(guān)聯(lián)對(duì)象
                 !isa.has_cxx_dtor  &&      // 對(duì)象有無自定義的C++ 析構(gòu)函數(shù)
                 !isa.has_sidetable_rc))    // 對(duì)象有無用到 sideTable 做引用計(jì)數(shù)
    {
        assert(!sidetable_present());
        // 都沒有,直接 free
        free(this);
    } 
    else {
        object_dispose((id)this);
    }
}

不能直接釋放凑懂,繼續(xù) --> 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++析構(gòu)函數(shù)關(guān)聯(lián)對(duì)象

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++的析構(gòu)函數(shù),則將C++析構(gòu)函數(shù)從類中銷毀
        if (cxx) object_cxxDestruct(obj);
        // 有關(guān)聯(lián)對(duì)象,將關(guān)聯(lián)對(duì)象全部移除摆碉,并將自己也從 assocationManager的map中移除
        if (assoc) _object_remove_assocations(obj);
        
        // 繼續(xù) dealloc 處理對(duì)象的其他引用  
        obj->clearDeallocating();
    }

    return obj;
}

2.2塘匣、銷毀其他

objc_object::clearDeallocating()

inline void 
objc_object::clearDeallocating()
{
    // 1. 要釋放的對(duì)象沒有采用優(yōu)化過的isa引用計(jì)數(shù)
    if (slowpath(!isa.nonpointer)) {
        // Slow path for raw pointer isa.
        sidetable_clearDeallocating();
    }
    // 2. 要釋放的對(duì)象采用優(yōu)化過的isa引用計(jì)數(shù) 且 有弱引用 或 有用sidetable來引用計(jì)數(shù)
    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、對(duì)象沒有采用優(yōu)化過的isa引用計(jì)數(shù)

sidetable_clearDeallocating():

void 
objc_object::sidetable_clearDeallocating()
{
    // 獲取當(dāng)前對(duì)象的 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();
    // 在散列表中找到對(duì)應(yīng)的引用計(jì)數(shù)表 RefcountMap巷帝,拿到要釋放的對(duì)象的引用計(jì)數(shù)
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it != table.refcnts.end()) {
        if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
            // 從 weak_table 中忌卤,找到要釋放對(duì)象的 entry,對(duì)entry中referrers遍歷將數(shù)組中的弱引用 全部置為 nil
            weak_clear_no_lock(&table.weak_table, (id)this);
        }
        // 從引用計(jì)數(shù)表中 擦除當(dāng)前對(duì)象的引用計(jì)數(shù)
        table.refcnts.erase(it);
    }
    table.unlock();
}

2.2.2锅睛、對(duì)象采用了優(yōu)化過的isa引用計(jì)數(shù)

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

    // 獲取當(dāng)前對(duì)象散列表 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的輔助引用計(jì)數(shù),直接在SideTable中擦除該對(duì)象的引用計(jì)數(shù)
        table.refcnts.erase(this);
    }
    table.unlock();
}

弱引用了對(duì)象的 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) 
{
    // 當(dāng)前被引用對(duì)象
    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;
    
    // 弱引用該對(duì)象的 所有weak指針地址 的數(shù)組 referrers
    // referrers 數(shù)組的 count
    if (entry->out_of_line()) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } 
    else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    
    // 遍歷现拒,將referrers數(shù)組內(nèi)的 weak指針 全部置為 nil
    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[i];
        if (referrer) {
            
            // 如果weak指針 弱引用了對(duì)象 referent,則將weak指針設(shè)置為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中移除對(duì)象的entry
    weak_entry_remove(weak_table, entry);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末望侈,一起剝皮案震驚了整個(gè)濱河市印蔬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌脱衙,老刑警劉巖侥猬,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捐韩,居然都是意外死亡退唠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門荤胁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瞧预,“玉大人,你說我怎么就攤上這事仅政」赣停” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵圆丹,是天一觀的道長滩愁。 經(jīng)常有香客問我,道長辫封,這世上最難降的妖魔是什么硝枉? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮倦微,結(jié)果婚禮上妻味,老公的妹妹穿的比我還像新娘。我一直安慰自己璃诀,他們只是感情好弧可,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般棕诵。 火紅的嫁衣襯著肌膚如雪裁良。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天校套,我揣著相機(jī)與錄音价脾,去河邊找鬼。 笑死笛匙,一個(gè)胖子當(dāng)著我的面吹牛侨把,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播妹孙,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼秋柄,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了蠢正?” 一聲冷哼從身側(cè)響起骇笔,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嚣崭,沒想到半個(gè)月后笨触,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡雹舀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年芦劣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片说榆。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡虚吟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出娱俺,到底是詐尸還是另有隱情稍味,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布荠卷,位于F島的核電站模庐,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏油宜。R本人自食惡果不足惜掂碱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望慎冤。 院中可真熱鬧瘦穆,春花似錦例嘱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間立磁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國打工剥槐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唱歧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓粒竖,卻偏偏與公主長得像颅崩,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蕊苗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容