iOS - weak 底層原理

[toc]

參考

weak

https://blog.csdn.net/future_one/article/details/81606895

https://www.cnblogs.com/guohai-stronger/p/10161870.html

http://www.itdecent.cn/p/2b12666b351f

問(wèn)答

weak作用?

weak 是弱引用, 用weak來(lái)修飾、描述所引用對(duì)象的計(jì)數(shù)器并不會(huì)加1, 而且weak會(huì)在引用對(duì)象被釋放的時(shí)候自動(dòng)置為nil, 這也就避免了野指針訪問(wèn)壞內(nèi)存而引起崩潰的情況,

weak也可以解決循環(huán)引用。

為什么修飾代理使用 weak 而不是用assign?

assign 可用來(lái)修飾基本數(shù)據(jù)類型, 也可修飾OC的對(duì)象, 但如果用 assign 修飾對(duì)象類型指向的是一個(gè)強(qiáng)指針, 當(dāng)指向的這個(gè)指針釋放之后, 它仍指向這塊內(nèi)存, 必須要手動(dòng)給置為nil, 否則會(huì)產(chǎn)生野指針, 如果還通過(guò)此指針操作那塊內(nèi)存, 會(huì)導(dǎo)致 EXC_BAD_ACCESS 錯(cuò)誤, 調(diào)用了已經(jīng)被釋放的內(nèi)存空間;

而 weak 只能用來(lái)修飾OC對(duì)象, 而且相比assign比較安全, 如果指向的對(duì)象消失了, 那么它會(huì)自動(dòng)置為nil, 不會(huì)導(dǎo)致野指針。

weak 本質(zhì)?
ARC幫我們做了什么?

ARC 是 LLVM + Runtime 互相協(xié)作的結(jié)果

開(kāi)啟ARC后, LLVM編譯器會(huì)自動(dòng)幫我們?cè)谙鄳?yīng)位置生成 release、retain、autorelease

在作用域 (大括號(hào)) 結(jié)束的位置添加 release

弱引用這種, 是通過(guò)runtime處理的

編譯解析

OC代碼
int main(){
    NSObject *obj = [[NSObject alloc] init];
    id __weak obj1 = obj;
}
C++代碼
int main(){
    NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
    id __attribute__((objc_ownership(weak))) obj1 = obj;
}

編譯之后的weak, 通過(guò) objc_ownership(weak) 實(shí)現(xiàn) weak 方法, objc_ownership 字面意思是:獲得對(duì)象的所有權(quán), 是對(duì)對(duì)象weak的初始化的一個(gè)操作。

通過(guò) weak 編譯解析, 可以看出 weak 是通過(guò) runtime 初始化并維護(hù)的;

weak 和 strong 都是 OC ARC 的修飾詞, 而 strong 是通過(guò) runtime 維護(hù)的一個(gè)自動(dòng)計(jì)數(shù)表結(jié)構(gòu)。

結(jié)構(gòu)

weak_table_t
  • weak_table_t 存儲(chǔ)在 struct SideTable 中。(參考《isa指針》)
  • weak_table_t 是 Runtime 維護(hù)的一個(gè) weak 引用的全局hash表, 用于存儲(chǔ)指向某個(gè)對(duì)象的所有weak指針?!?
    • key: 所指向?qū)ο蟮牡刂?/li>
    • value: weak_entry_t 類型結(jié)構(gòu)體數(shù)組(weak指針的數(shù)組) 。 value 是數(shù)組的原因是: 因?yàn)橐粋€(gè)對(duì)象可能被多個(gè)弱引用指針指向 ★

weak 指針的地址值是所指對(duì)象指針的地址

// objc-weak.h
/**
 * The global weak references table.  全局的, 內(nèi)部維護(hù)了一個(gè)哈希表 ★
 * Stores object ids as keys, and weak_entry_t structs as their values.
 */
struct weak_table_t {
    weak_entry_t *weak_entries; // 保存了所有指向指定對(duì)象的weak指針   weak_entries的對(duì)象
    size_t    num_entries; // 弱引用數(shù)量
    uintptr_t mask;
    uintptr_t max_hash_displacement;
};
weak_entry_t

weak 全局表 weak_table_t 中的存儲(chǔ) weak 定義的對(duì)象的表結(jié)構(gòu) weak_entry_t,

weak_entry_t 是存儲(chǔ)在弱引用表中的一個(gè)內(nèi)部結(jié)構(gòu)體, 它負(fù)責(zé)維護(hù)和存儲(chǔ)指向一個(gè)對(duì)象的所有弱引用hash表

// 單個(gè)弱引用, 存放在哈希表中
struct weak_entry_t {
    // 對(duì)泛型對(duì)象的指針做了一個(gè)封裝, 通過(guò)這個(gè)泛型類來(lái)解決內(nèi)存泄漏的問(wèn)題。
    // 相當(dāng)于 objc_object *referent;
    DisguisedPtr<objc_object> referent;
    union {
        struct {
            // weak_referrer_t 是 objc_object ** 的別名, 通過(guò)一個(gè)二維指針地址偏移, 用下標(biāo)作為 hash 的 key, 做成了一個(gè)弱引用散列。
            weak_referrer_t *referrers; // weak變量地址
            uintptr_t        out_of_line_ness : 2;
            // 引用數(shù)值。這里記錄弱引用表中引用有效數(shù)字, 因?yàn)槿跻帽硎褂玫氖庆o態(tài) hash 結(jié)構(gòu), 所以需要使用變量來(lái)記錄數(shù)目。
            uintptr_t        num_refs : PTR_MINUS_2; 
            uintptr_t        mask;
            // hash 元素上限閥值。
            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];
        };
    };

    // 最低有效位, 也是標(biāo)志位。當(dāng)標(biāo)志位 0 時(shí), 增加引用表指針緯度。
    // 當(dāng)其為0的時(shí)候,  weak_referrer_t 成員將擴(kuò)展為多行靜態(tài) hash table。
    // 通常情況下是等于零的, 所以弱引用表總是一個(gè) objc_objective 指針二維數(shù)組。
    // 一維 objc_objective 指針可構(gòu)成一張弱引用散列表, 通過(guò)第三緯度實(shí)現(xiàn)了多張散列表, 并且表數(shù)量為 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;
    }
    // objc_object: weak_entry_t對(duì)象中的范型對(duì)象, 用于標(biāo)示weak引用的對(duì)象。
    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;
        }
    }
};

#define WEAK_INLINE_COUNT 4
#define REFERRERS_OUT_OF_LINE 2


weak_referrer_t
// The address of a __weak variable.
// These pointers are stored disguised(偽裝的) so memory analysis tools
// don't see lots of interior(內(nèi)部的) pointers from the weak table into objects.
typedef DisguisedPtr<objc_object *> weak_referrer_t;

/* 蘋(píng)果對(duì) DisguisedPtr 的注釋
DisguisedPtr<T> acts like pointer type T*, except the stored value is disguised to hide it from tools like `leaks`.
nil is disguised as itself so zero-filled memory works as expected, 
which means 0x80..00 is also disguised as itself but we don't care.
Note that weak_entry_t knows about this encoding.
*/

可以看到 DisguisedPtr<objc_object *> 相當(dāng)于 objc_object **;

weak_referrer_t 就是 objc_object ** 的別名

存儲(chǔ)過(guò)程

初始化weak變量時(shí), runtime會(huì)調(diào)用 objc_initWeak 函數(shù), 初始化新的weak指針指向?qū)ο蟮牡刂?

objc_initWeak 函數(shù)內(nèi)部會(huì)調(diào)用 objc_storeWeak() 函數(shù), objc_storeWeak() 函數(shù)的作用是用來(lái)更新指針的指向, 創(chuàng)建弱引用表。

objc_initWeak
id objc_initWeak(id *location, id newObj) {
    // 首先判斷指針指向的類對(duì)象是否有效, 若無(wú)效直接釋放指針并返回;
    if (!newObj) {
        *location = nil;
        return nil;
    }
    // 注冊(cè)為一個(gè)指向value的_weak對(duì)象
    // 更新指針的指向, 創(chuàng)建弱引用表。
    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object*)newObj);
}
storeWeak()
// 更新指針指向, 創(chuàng)建對(duì)應(yīng)弱引用表
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:
    // 獲取舊引用散列
    if (haveOld) {
        oldObj = *location;
        oldTable = &SideTables()[oldObj];
    } else {
        oldTable = nil;
    }
    // 獲取新引用散列
    if (haveNew) {
        newTable = &SideTables()[newObj];
    } else {
        newTable = nil;
    }
    // 加鎖操作, 解決選擇競(jìng)爭(zhēng); 
    // 為了解決死鎖問(wèn)題, 可能會(huì)開(kāi)啟二次嘗試
    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.
    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;
        }
    }
    // 解除舊對(duì)象與弱引用表關(guān)聯(lián)綁定
    // Clean up old value, if any.
    if (haveOld) {
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }
    
    // 構(gòu)造新的弱引用表
    // Assign new value, if any.
    if (haveNew) {
        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()) {
            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;
}
weak_unregister_no_lock()
/** 
 * Unregister an already-registered weak reference.
 * This is used when referrer's storage is about to go away, but referent
 * isn't dead yet. (Otherwise, zeroing referrer later would be a
 * bad memory access.)
 * Does nothing if referent/referrer is not a currently active weak reference.
 * Does not zero referrer.
 * 
 * FIXME currently requires old referent value to be passed in (lame)
 * FIXME unregistration should be automatic if referrer is collected
 * 
 * @param weak_table The global weak table.
 * @param referent The object.
 * @param referrer The weak reference.
 */
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;

    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        remove_referrer(entry, referrer);
        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;
                }
            }
        }

        if (empty) {
            weak_entry_remove(weak_table, entry);
        }
    }

    // Do not set *referrer = nil. objc_storeWeak() requires that the 
    // value not change.
}
weak_register_no_lock()
/** 
 * 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.
 */
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;

    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;
        }
    }
    // 構(gòu)造新的弱引用表
    // now remember it and where it is being stored
    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        append_referrer(entry, referrer);
    } 
    else {
        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;
}

在最后會(huì)調(diào)用 clearDeallocating 函數(shù)。而 clearDeallocating 函數(shù)首先根據(jù)對(duì)象的地址獲取weak指針地址的數(shù)組, 然后緊接著遍歷這個(gè)數(shù)組, 將其中的數(shù)組開(kāi)始置為nil, 把這個(gè) entry從weak 表中刪除, 最后一步清理對(duì)象的記錄。

釋放過(guò)程

當(dāng) weak 指向的對(duì)象被釋放時(shí), 編譯器如何讓 weak 指針置為nil的呢?

將弱引用存到一個(gè)弱引用表(哈希表)中, 對(duì)象銷毀時(shí), 就從表中取出當(dāng)前對(duì)象的弱引用并清除

  1. 調(diào)用 objc_release

  2. 因?yàn)閷?duì)象的引用計(jì)數(shù)為0, 所以執(zhí)行 dealloc

  3. 在 dealloc 中, 調(diào)用了 _objc_rootDealloc 函數(shù)

  4. _objc_rootDealloc 中, 調(diào)用了object_dispose 函數(shù)

  5. 調(diào)用 objc_destructInstance

  6. 最后調(diào)用objc_clear_deallocating,詳細(xì)過(guò)程如下:

    a. 從weak表中獲取廢棄對(duì)象的地址為鍵值的記錄

    b. 將包含在記錄中的所有附有 weak修飾符變量的地址, 賦值為 nil

    c. 將weak表中該記錄刪除

    d. 從引用計(jì)數(shù)表中刪除廢棄對(duì)象的地址為鍵值的記錄

釋放過(guò)程 - 實(shí)驗(yàn)代碼

@implementation Person
- (void)dealloc {
    NSLog(@"%s", __func__);
}
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    __strong Person *person1;
    __weak Person *person2;
    __unsafe_unretained Person *person3;
    
    NSLog(@"111");
    
    {
        Person *person = [[Person alloc] init];
        person2 = person;
        person3 = person;
    }
    
    NSLog(@"222 - %@", person2); // 打印 null , 說(shuō)明弱指針 person2 在其指向的對(duì)象銷毀時(shí), 會(huì)自動(dòng)置空
    NSLog(@"333 - %@", person3); // 會(huì)報(bào)壞EXC_BAD_ACCESS內(nèi)存訪問(wèn), 在這里person3依然有值, 只不過(guò)是個(gè)野指針, 指向的person對(duì)象已被銷毀
}
@end

釋放過(guò)程 - 源碼

注: 關(guān)于 SideTable、weak_table_tweak_entry_t 參考 《isa指針

dealloc
// Replaced by CF (throws an NSException)
+ (void)dealloc {
}

// Replaced by NSZombies
- (void)dealloc {
    _objc_rootDealloc(self);
}
rootDealloc()
void _objc_rootDealloc(id obj) {
    ASSERT(obj);
    obj->rootDealloc();
}

inline void objc_object::rootDealloc() {
    if (isTaggedPointer()) return;  // fixme necessary?
    
    // 如果該對(duì)象是優(yōu)化的isa(arm64)且 沒(méi)有 弱引用 / 關(guān)聯(lián)對(duì)象 / C++析構(gòu)器 / 引用計(jì)數(shù)表, 直接釋放
    if (fastpath(isa.nonpointer  &&  
                 !isa.weakly_referenced  &&  
                 !isa.has_assoc  &&  
                 !isa.has_cxx_dtor  &&  
                 !isa.has_sidetable_rc)) {
        assert(!sidetable_present());
        free(this);
    } 
    // 普通isa不管有沒(méi)有用過(guò)上述資源, 都走這里
    // 需要釋放該對(duì)象相關(guān)的其他資源
    else {
        object_dispose((id)this);
    }
}
object_dispose()
// 有弱引用必然會(huì)走這里
id object_dispose(id obj) {
    if (!obj) return nil;
    // 釋放 obj 相關(guān)資源
    objc_destructInstance(obj); 
    // 釋放自身
    free(obj);

    return nil;
}
objc_destructInstance()
// 釋放 obj 相關(guān)資源
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); // 清除c++析構(gòu)函數(shù)相關(guān)成員變量
        if (assoc) _object_remove_assocations(obj); // 移除關(guān)聯(lián)對(duì)象
        
        // 根據(jù)當(dāng)前對(duì)象的地址值, 找到對(duì)應(yīng)的
        obj->clearDeallocating(); // 將指向當(dāng)前對(duì)象的 weak 弱指針置為nil ★
    }
    return obj;
}
clearDeallocating()
// 
inline void objc_object::clearDeallocating() {
    // 普通isa指針, 直接指向 class / metaclass 
    if (slowpath(!isa.nonpointer)) { 
        // Slow path for raw pointer isa.
        sidetable_clearDeallocating();
    }
    // arm64優(yōu)化過(guò)的isa
    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());
}
clearDeallocating_slow()
// arm64優(yōu)化過(guò)的isa 走這里
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) {
        // SideTable 有個(gè)弱引用表 weak_table, 是個(gè)哈希表 
        // (SideTable 數(shù)據(jù)結(jié)構(gòu)可到《isa》中查看)
        weak_clear_no_lock(&table.weak_table, (id)this);
    }
    // 引用計(jì)數(shù)長(zhǎng)度超出19bit, 存放在 SideTable
    if (isa.has_sidetable_rc) {
        // 清除引用計(jì)數(shù)
        table.refcnts.erase(this);
    }
    table.unlock();
}
weak_clear_no_lock()
/** 
 * 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) {
    // 要銷毀的對(duì)象
    objc_object *referent = (objc_object *)referent_id;
    // 根據(jù) 要銷毀的對(duì)象地址值 獲取 弱引用入口
    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()) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } 
    else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    
    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[i];
        if (referrer) {
            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_entry_remove(weak_table, entry);
}
weak_entry_for_referent()
/** 根據(jù) 要銷毀的對(duì)象地址值 獲取 弱引用入口
 * Return the weak reference table entry for the given referent. 
 * If there is no entry for referent, return NULL. 
 * Performs a lookup.
 *
 * @param weak_table 
 * @param referent The object. Must not be nil.
 * 
 * @return The table of weak referrers to this object. 
 */
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;
    
    // 用 以要銷毀的對(duì)象地址值 referent, 按位與 weak_table的掩碼, 得出哈希表索引
    // 可以看出, weak_table 內(nèi)部有一個(gè)哈希表 weak_entries
    size_t begin = hash_pointer(referent) & weak_table->mask;
    size_t index = begin;
    size_t hash_displacement = 0;
    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;
        }
    }
    // 從哈希表中取出index對(duì)應(yīng)的弱引用
    return &weak_table->weak_entries[index];
}
weak_entry_remove()
/**
 * Remove entry from the zone's table of weak references.
 */
static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry) {
    // remove entry
    // 釋放weak變量地址, entry->referrers ★★
    if (entry->out_of_line()) free(entry->referrers);
    // 釋放entry
    bzero(entry, sizeof(*entry));
    // 弱引用數(shù)量-1
    weak_table->num_entries--;

    weak_compact_maybe(weak_table);
}

// 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
    }
}
sidetable_clearDeallocating
// arm64之前的 普通isa 使用該方法釋放
void objc_object::sidetable_clearDeallocating() {
    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::iterator it = table.refcnts.find(this);
    if (it != table.refcnts.end()) {
        if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
            weak_clear_no_lock(&table.weak_table, (id)this);
        }
        table.refcnts.erase(it);
    }
    table.unlock();
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容