iOS底層學習 - 內存管理之weak原理探究

內存管理在APP開發過程當中佔據着一個很重要的地位,在iOS中,系統爲咱們提供了ARC的開發環境,幫助咱們作了不少內存管理的內容。本章咱們先來看一下,平時開發中使用最多的weak在底層是如何進行實現的數組

weak做用

咱們經過例子🌰來看一下__strong__weak__unsafe_unretained的區別在哪裏.安全

首先是看以下例子,能夠知道在臨時做用域結束以後,生成的對象就會進行銷燬,咱們在做用域外部用修飾符來持有對象,再來看一下對象的銷燬狀況多線程

NSLog(@"臨時做用域開始");
{
    LGPerson *person = [[LGPerson alloc] init];
    NSLog(@"person對象:%@", person);
}
NSLog(@"臨時做用域結束");

***************************打印結果******************************
2020-01-19 10:57:13.910542+0800 objc-debug[74175:19740208] 臨時做用域開始
2020-01-19 10:57:13.911181+0800 objc-debug[74175:19740208] person對象:<LGPerson: 0x10221c900>
2020-01-19 10:57:13.911277+0800 objc-debug[74175:19740208] LGPerson -[LGPerson dealloc]
2020-01-19 10:57:13.911367+0800 objc-debug[74175:19740208] 臨時做用域結束
複製代碼

先來看一下用__strong修飾的結果。能夠發現修飾的對象在做用域結束以後並無銷燬,說明該對象的引用計數增長了app

__strong LGPerson *strongPerson;
NSLog(@"臨時做用域開始");
{
    LGPerson *person = [[LGPerson alloc] init];
    NSLog(@"person對象:%@", person);
    strongPerson = person;
}
NSLog(@"臨時做用域結束");
NSLog(@"strongPerson:%@", strongPerson);
***************************打印結果******************************
2020-01-19 11:54:44.079292+0800 objc-debug[74452:19777011] 臨時做用域開始
2020-01-19 11:54:44.080060+0800 objc-debug[74452:19777011] person對象:<LGPerson: 0x101945ae0>
2020-01-19 11:54:44.080172+0800 objc-debug[74452:19777011] 臨時做用域結束
2020-01-19 11:54:44.080292+0800 objc-debug[74452:19777011] strongPerson:<LGPerson: 0x101945ae0>

複製代碼

再來看一下__weak修飾的結果。經過下面的運行咱們能夠發現,用__weak修飾後,並無增長引用計數,而且做用域結束,對象釋放後,修飾的對象爲nil,沒有形成野指針的崩潰,能夠說是一種安全的方案ide

__weak LGPerson *weakPerson;
NSLog(@"臨時做用域開始");
{
    LGPerson *person = [[LGPerson alloc] init];
    NSLog(@"person對象:%@", person);
    weakPerson = person;
}
NSLog(@"臨時做用域結束");
NSLog(@"weakPerson:%@", weakPerson);

***************************打印結果******************************
2020-01-19 11:58:08.842409+0800 objc-debug[74479:19780263] 臨時做用域開始
2020-01-19 11:58:08.843151+0800 objc-debug[74479:19780263] person對象:<LGPerson: 0x101712030>
2020-01-19 11:58:08.843382+0800 objc-debug[74479:19780263] LGPerson -[LGPerson dealloc]
2020-01-19 11:58:08.843572+0800 objc-debug[74479:19780263] 臨時做用域結束
2020-01-19 11:58:08.843762+0800 objc-debug[74479:19780263] weakPerson:(null)
複製代碼

最後咱們來看一下,平時開發使用較少的__unsafe_unretained和上面兩個的區別在哪。咱們經過結果能夠發現,在做用域消失,對象就進行了銷燬,而且在出做用域打印修飾對象時,出現了野指針的崩潰EXC_BAD_ACCESS函數

因此這樣就看出了__weak__unsafe_unretained的區別就是前者會在對象被釋放的時候自動置爲nil,然後者卻不行。優化

__unsafe_unretained LGPerson *unsafePerson;
NSLog(@"臨時做用域開始");
{
    LGPerson *person = [[LGPerson alloc] init];
    NSLog(@"person對象:%@", person);
    unsafePerson = person;
}
NSLog(@"臨時做用域結束");
NSLog(@"unsafePerson:%@", unsafePerson);

***************************打印結果******************************
2020-01-19 12:02:34.428120+0800 objc-debug[74513:19785153] 臨時做用域開始
2020-01-19 12:02:34.428813+0800 objc-debug[74513:19785153] person對象:<LGPerson: 0x1019159f0>
2020-01-19 12:02:34.428901+0800 objc-debug[74513:19785153] LGPerson -[LGPerson dealloc]
2020-01-19 12:02:34.429015+0800 objc-debug[74513:19785153] 臨時做用域結束
複製代碼

小結

  • __strong修飾後,對象的引用計數會增長,在做用域外不會銷燬
  • __weak修飾後,對象引用計數不會增長,在做用域外會自動置爲nil
  • __unsafe_unretained修飾後,引用計數不會增長,在做用域外不會置空,會形成野指針崩潰

經過上面例子基本瞭解了__weak的做用,那麼__weak是如何進行建立和銷燬的呢,下面經過源碼進行深度探索 ui

weak的建立

仍是使用剛纔的例子,直接跟蹤彙編和打符號斷點,發現底層庫調了objc_initWeak函數this

objc_initWeak

其中兩個參數locationnewObj的含義以下spa

  • location:表示__weak指針的地址,即例子中的weak指針取地址: &weakObjc 。它是一個指針的地址。之因此要存儲指針的地址,是由於最後咱們要講__weak指針指向的內容置爲nil,若是僅存儲指針的話,是不可以完成這個功能的。
  • newObj:所引用的對象,即例子中的person
id objc_initWeak(id *location, id newObj) {
    if (!newObj) {
        *location = nil;
        return nil;
    }

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

storeWeak

查看storeWeak源碼,根據註釋,能夠知道以下幾點

  • HaveOldweak指針以前是否已經指向了一個弱引用
  • HaveNewweak指針是否須要指向一個新引用
  • CrashIfDeallocating:若是被弱引用的對象正在析構,此時再弱引用該對象,是否應該crash
// 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;
    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;
        oldTable = &SideTables()[oldObj];
    } else {
        // 沒有弱引用過,則oldTable = nil
        oldTable = nil;
    }
    ✅// 若是weak指針要弱引用一個新的obj,則將該obj對應的SideTable取出,賦值給newTable
    if (haveNew) {
        newTable = &SideTables()[newObj];
    } else {
        newTable = nil;
    }
    ✅// 加鎖操做,防止多線程中競爭衝突
    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
    ✅// location 應該與 oldObj 保持一致,若是不一樣,說明當前的 location 已經處理過 oldObj 但是又被其餘線程所修改
    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.
    if (haveNew  &&  newObj) {
        Class cls = newObj->getIsa();
        ✅// 若是cls尚未初始化,先初始化,再嘗試設置弱引用
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized()) 
        {
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            _class_initialize(_class_getNonMetaClass(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;
            ✅// newObj 初始化後,從新獲取一遍newObj
            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.// 若是weak指針須要弱引用新的對象newObj
    if (haveNew) {
       ✅ // 調用weak_register_no_lock方法,將weak指針的地址記錄到newObj對應的weak_entry_t中
        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.// 更新newObj的isa指針的weakly_referenced bit標誌位
        if (newObj  &&  !newObj->isTaggedPointer()) {
            newObj->setWeaklyReferenced_nolock();
        }

        // Do not set *location anywhere else. That would introduce a race.// *location 賦值,也就是將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

接着咱們來分析weak_register_no_lock函數,是怎麼註冊弱引用的。

咱們發現函數內部主要進行了isTaggedPointerdeallocating的判斷等前置條件,這些都是不能進行弱引用的狀況。

若是能夠被弱引用,則將被弱引用對象所在的weak_table中weak_entry_t哈希數組中取出對應的weak_entry_t,若是weak_entry_t不存在,則會新建一個。而後將指向被弱引用對象地址的指針referrer經過函數append_referrer插入到對應的weak_entry_t引用數組。至此就完成了弱引用。

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()) {
        deallocating = referent->rootIsDeallocating();
    }
    else {
        BOOL (*allowsWeakReference)(objc_object *, SEL) = 
            (BOOL(*)(objc_object *, SEL))
            object_getMethodImplementation((id)referent, 
                                           SEL_allowsWeakReference);
        if ((IMP)allowsWeakReference == _objc_msgForward) {
            return nil;
        }
        deallocating =
            ! (*allowsWeakReference)(referent, SEL_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_table 中找到被弱引用對象 referent 對應的 weak_entry,並將 referrer 加入到 weak_entry 中
    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        ✅// 若是能找到 weak_entry,則講 referrer 插入到 weak_entry 中
        append_referrer(entry, referrer);
    } 
    else {
        ✅// 若是找不到 weak_entry,就新建一個
        weak_entry_t new_entry(referent, referrer);
        weak_grow_maybe(weak_table);
        weak_entry_insert(weak_table, &new_entry);
    }

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

    return referent_id;
}
複製代碼

append_referrer

這一步主要是找到弱引用對象的對應的weak_entry哈希數組中,基本就是個遍歷插入的過程,原理比較簡單

static void append_referrer(weak_entry_t *entry, objc_object **new_referrer) {
    ✅// 若是weak_entry 使用靜態數組 inline_referrers
    if (! entry->out_of_line()) {
        // Try to insert inline.// 嘗試將 referrer 插入數組
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            if (entry->inline_referrers[i] == nil) {
                entry->inline_referrers[i] = new_referrer;
                return;
            }
        }

        // Couldn't insert inline. Allocate out of line.// 若是inline_referrers的位置已經存滿了,則要轉型爲 referrers,動態數組
        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.
        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());
    ✅// 若是動態數組中元素個數大於或等於數組總空間的3/4,則擴展數組空間爲當前長度的一倍,而後將 referrer 插入數組
    if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
        return grow_refs_and_insert(entry, new_referrer);
    }
    ✅// 若是不須要擴容,直接插入到weak_entry中// & (entry->mask) 保證 begin 的位置只能大於或等於數組的長度
    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++;
}
複製代碼

weak_unregister_no_lock

若是weak指針在指向obj以前,已經弱引用了其餘的對象,則須要先將weak指針從其餘對象的weak_entry_thash數組中移除。在storeWeak方法中會調用weak_unregister_no_lock函數來作移除操做,咱們來看一下weak_unregister_no_lock函數源碼

weak_unregister_no_lock函數首先會在weak_table中找出之前被弱引用的對象referent對應的weak_entry_t,在weak_entry_t中移除被弱引用的對象referrer。移除元素後,判斷此時weak_entry_t中是否還有元素。若是此時weak_entry_t已經沒有元素了,則須要將weak_entry_tweak_table中移除。

void weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, id *referrer_id) {
    ✅// 拿到之前弱引用的對象和對象的地址
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;

    weak_entry_t *entry;

    if (!referent) return;
    ✅// 查找到之前弱引用的對象 referent 所對應的 weak_entry_t
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        ✅// 在之前弱引用的對象 referent 所對應的 weak_entry_t 的 hash 數組中,移除弱引用 referrer
        remove_referrer(entry, referrer);
        ✅// 移除元素以後, 要檢查一下 weak_entry_t 的 hash 數組是否已經空了
        bool empty = true;
        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;
                }
            }
        }
        ✅// 若是 weak_entry_t 的hash數組已經空了,則須要將 weak_entry_t 從 weak_table 中移除
        if (empty) {
            weak_entry_remove(weak_table, entry);
        }
    }

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

至此,一個對象的弱引用過程已經結束

weak的銷燬

經過開頭的例子,咱們知道,出做用域,對象dealloc後,會自動把弱引用對象置空,那麼他是怎麼實現的,咱們能夠簡單查看下類的dealloc流程

_objc_rootDealloc

- (void)dealloc {
    _objc_rootDealloc(self);
}
**********************************
void _objc_rootDealloc(id obj)
{
    assert(obj);

    obj->rootDealloc();
}
***********************************
inline void objc_object::rootDealloc()
{
    //✅若是是Tagged Pointer,就直接返回
    if (isTaggedPointer()) return;  // fixme necessary?

    /*
    ✅若是同時知足 
    1. 是優化過的isa、
    2. 沒有被weak指針引用過、
    3. 沒有關聯對象、
    4. 沒有C++析構函數、
    5. 沒有sideTable,
    就能夠直接釋放內存free()
    */
    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);
    }
}
複製代碼

咱們這裏顯然不知足上述條件,由於咱們弱引用過,繼續跟進object_dispose

object_dispose

object_dispose函數中調用了objc_destructInstance

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

    objc_destructInstance(obj);    
    free(obj);

    return nil;
}
***********************************
複製代碼

objc_destructInstance

咱們能夠看到內部會作銷燬C++析構函數以及移除關聯對象的操做,看來弱引用要在clearDeallocating中了

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); 
        ✅// 若是有關聯對象,則移除全部的關聯對象,並將其自身從Association Manager的map中移除
        if (assoc) _object_remove_assocations(obj); 
        ✅// 繼續清理其它相關的引用
        obj->clearDeallocating(); 
    }
    return obj;
}
複製代碼

clearDeallocating

inline void 
objc_object::clearDeallocating()
{
    if (slowpath(!isa.nonpointer)) {
        // Slow path for raw pointer isa.// 若是要釋放的對象沒有采用了優化過的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.// 若是要釋放的對象採用了優化過的isa引用計數,而且有弱引用或者使用了sideTable的輔助引用計數
        clearDeallocating_slow();
    }

    assert(!sidetable_present());
}
複製代碼

clearDeallocating_slow

咱們如今通常都是使用優化的isa引用計數,因此咱們以此爲目的繼續探索。咱們經過源碼能夠看到主要是操做爲找到對應的SideTable,而後再SideTableweak_table中,將弱引用對象置空,主要的方法爲weak_clear_no_lock

NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
    assert(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));
    ✅// 在全局的SideTables中,以this指針(要釋放的對象)爲key,找到對應的SideTable
    SideTable& table = SideTables()[this];
    table.lock();
    if (isa.weakly_referenced) {
        ✅//要釋放的對象被弱引用了,經過weak_clear_no_lock函數將指向該對象的弱引用指針置爲nil
        weak_clear_no_lock(&table.weak_table, (id)this);
    }
    ✅//使用了sideTable的輔助引用計數,直接在SideTable中擦除該對象的引用計數
    if (isa.has_sidetable_rc) {
        table.refcnts.erase(this);
    }
    table.unlock();
}
複製代碼

weak_clear_no_lock

咱們經過源碼能夠看到,這不方法和插入時的方法比較相似,都是找到對應的eak_entry_t數組,而後經過遍歷找到對應的指針地址,而後置爲nil,防止了野指針的報錯

void weak_clear_no_lock(weak_table_t *weak_table, id referent_id) {
    ✅//獲取被弱引用對象的地址
    objc_object *referent = (objc_object *)referent_id;
    ✅// 根據對象地址找到被弱引用對象referent在weak_table中對應的weak_entry_t
    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指針地址數組
    if (entry->out_of_line()) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } 
    else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    ✅// 遍歷取出每一個weak指針的地址
    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[i]; 
        if (referrer) {
            ✅// 若是weak指針確實弱引用了對象 referent,則將weak指針設置爲nil
            if (*referrer == referent) { 
                *referrer = nil;
            }
            ✅// 若是所存儲的weak指針沒有弱引用對象 referent,這多是因爲runtime代碼的邏輯錯誤引發的,報錯
            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_entry_remove(weak_table, entry);
}
複製代碼

至此,一個弱引用的銷燬也完成了,並自動置爲nil

總結

  • 當一個對象obj被weak指針指向時,這個weak指針會以obj做爲key,被存儲到sideTable類的weak_table這個散列表上對應的一個weak指針數組裏面。
  • 當一個對象obj的dealloc方法被調用時,Runtime會以obj爲key,從sideTableweak_table散列表中,找出對應的weak指針列表,而後將裏面的weak指針逐個置爲nil

建立流程簡圖

建立流程小結:

Runtime維護了一個弱引用表,將全部弱引用obj的指針地址都保存在obj對應的weak_entry_t中

  1. 建立時,先從找到全局散列表SideTables中對應的弱引用表weak_table
  2. weak_table中被弱引用對象的referent,並建立或者插入對應的weak_entry_t
  3. 而後append_referrer(entry, referrer)將個人新弱引⽤的對象加進去entry
  4. 最後weak_entry_insertentry加⼊到咱們的weak_table

銷燬流程簡圖

銷燬流程小結:

  1. 首先根據對象地址獲取全部weak指針地址的數組
  2. 而後遍歷這個數組把對應的數據清空置爲nil
  3. 同時,將weak_entry_t移除出弱引用表weak_table

參考

iOS底層原理探索 — weak實現原理

內存管理—— weak指針實現原理

相關文章
相關標籤/搜索