weak
https://blog.csdn.net/u013378438/article/details/82790332
Runtime 維護(hù)了一個(gè) weak表,用于存儲(chǔ)指向某個(gè)對(duì)象的所有weak指針粹湃。weak表 其實(shí)是一個(gè) hash(哈希)表刚操,Key 是所指對(duì)象的地址,Value是 weak指針 的地址(這個(gè)地址的值是所指對(duì)象指針的地址)數(shù)組再芋。
SideTables 有多個(gè) SideTable hash值到 一個(gè)
SideTable 內(nèi)部有 slock 引用計(jì)數(shù)表,weak_table
weak_table 內(nèi)部有 weak_entries坚冀, hash 到 對(duì)應(yīng) weak_entry_t
1济赎、初始化時(shí):runtime 會(huì)調(diào)用 objc_initWeak函數(shù),初始化一個(gè)新的 weak指針 指向?qū)ο蟮牡刂贰?br>
2记某、添加引用時(shí):objc_initWeak函數(shù) 會(huì)調(diào)用 objc_storeWeak() 函數(shù)司训, objc_storeWeak() 的作用是更新指針指向,創(chuàng)建對(duì)應(yīng)的弱引用表液南。
3壳猜、釋放時(shí),調(diào)用 clearDeallocating函數(shù)滑凉。clearDeallocating函數(shù)首先根據(jù)對(duì)象地址獲取所有 weak指針地址的數(shù)組统扳,然后遍歷這個(gè)數(shù)組把其中的數(shù)據(jù)設(shè)為 nil喘帚,最后把這個(gè) entry 從 weak表中刪除,最后清理對(duì)象的記錄咒钟。
struct SideTable {
// 保證原子操作的自旋鎖
spinlock_t slock;
// 引用計(jì)數(shù)的 hash 表
RefcountMap refcnts;
// weak 引用全局 hash 表
weak_table_t weak_table;
}
struct weak_table_t {
// 保存了所有指向指定對(duì)象的 weak 指針
weak_entry_t *weak_entries;
// 存儲(chǔ)空間
size_t num_entries;
// 參與判斷引用計(jì)數(shù)輔助量
uintptr_t mask;
// hash key 最大偏移值
uintptr_t max_hash_displacement;
};
struct weak_entry_t {
DisguisedPtr<objc_object> referent; // 被弱引用的對(duì)象
// 引用該對(duì)象的對(duì)象列表吹由,聯(lián)合。 引用個(gè)數(shù)小于4朱嘴,用inline_referrers數(shù)組倾鲫。 用個(gè)數(shù)大于4,用動(dòng)態(tài)數(shù)組weak_referrer_t *referrers
union {
struct {
weak_referrer_t *referrers; // 弱引用該對(duì)象的對(duì)象指針地址的hash數(shù)組
uintptr_t out_of_line_ness : 2; // 是否使用動(dòng)態(tài)hash數(shù)組標(biāo)記位
uintptr_t num_refs : PTR_MINUS_2; // hash數(shù)組中的元素個(gè)數(shù)
uintptr_t mask; // hash數(shù)組長(zhǎng)度-1萍嬉,會(huì)參與hash計(jì)算乌昔。(注意,這里是hash數(shù)組的長(zhǎng)度壤追,而不是元素個(gè)數(shù)磕道。比如,數(shù)組長(zhǎng)度可能是64大诸,而元素個(gè)數(shù)僅存了2個(gè))素個(gè)數(shù))捅厂。
uintptr_t max_hash_displacement; // 可能會(huì)發(fā)生的hash沖突的最大次數(shù),用于判斷是否出現(xiàn)了邏輯錯(cuò)誤(hash表中的沖突次數(shù)絕不會(huì)超過(guò)改值)
};
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) // 構(gòu)造方法资柔,里面初始化了靜態(tài)數(shù)組
{
inline_referrers[0] = newReferrer;
for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
inline_referrers[i] = nil;
}
}
};
————————————————
dealloc
1.首先調(diào)用 _objc_rootDealloc()
2.接下來(lái)調(diào)用 rootDealloc()
3.這時(shí)候會(huì)判斷是否可以被釋放焙贷,判斷的依據(jù)主要有 5 個(gè),判斷是否有以上五種情況
? NONPointer_ISA? weakly_reference ? has_assoc? has_cxx_dtor? has_sidetable_rc
4-1.如果有以上五中任意一種贿堰,將會(huì)調(diào)用 object_dispose()方法辙芍,做下一步的處理。
4-2.如果沒(méi)有之前五種情況的任意一種羹与,則可以執(zhí)行釋放操作故硅,C 函數(shù)的 free()。
5.執(zhí)行完畢纵搁。
2.object_dispose() 調(diào)用流程吃衅。
? 1.直接調(diào)用 objc_destructInstance()。
? 2.之后調(diào)用 C 函數(shù)的 free()腾誉。
3.objc_destructInstance() 調(diào)用流程
? 1.先判斷 hasCxxDtor徘层,如果有 C++ 的相關(guān)內(nèi)容,要調(diào)用 object_cxxDestruct() 利职,銷毀 C++ 相關(guān)的內(nèi)容趣效。
? 2.再判斷hasAssocitatedObjects,如果有的話猪贪,要調(diào)用object_remove_associations()跷敬, 銷毀關(guān)聯(lián)對(duì)象的一系列操作。
? 3.然后調(diào)用 clearDeallocating()热押。
? 4.執(zhí)行完畢西傀。
4.clearDeallocating() 調(diào)用流程斤寇。
? 1.先執(zhí)行 sideTable_clearDellocating()。
? 2.再執(zhí)行 weak_clear_no_lock,在這一步驟中池凄,會(huì)將指向該對(duì)象的弱引用指針置為 nil抡驼。
? 3.接下來(lái)執(zhí)行 table.refcnts.eraser(),從引用計(jì)數(shù)表中擦除該對(duì)象的引用計(jì)數(shù)肿仑。
? 4.至此為止致盟,Dealloc 的執(zhí)行流程結(jié)束。