轉(zhuǎn)載自:【iOS】weak的底層實(shí)現(xiàn)
weak底層千千萬,吾竟裝作看不見...
weak基本用法
weak是弱引用,用weak描述修飾或者所引用對象的計數(shù)器不會加一,并且會在引用的對象被釋放的時候自動被設(shè)置為nil,大大避免了野指針訪問壞內(nèi)存引起崩潰的情況,另外weak還可以用于解決循環(huán)引用唆姐。
weak原理概括
weak表其實(shí)是一個hash(哈希)表,Key是所指對象的地址廓八,Value是weak指針的地址數(shù)組奉芦。weak的底層實(shí)現(xiàn)的原理是什么赵抢?
Runtime維護(hù)了一個weak表,用于存儲指向某個對象的所有weak指針声功。weak表其實(shí)是一個hash表烦却,Key是所指對象的地址,value是weak指針的地址(這個地址的值是所指對象指針的地址)數(shù)組先巴。
為什么value是數(shù)組其爵?因為一個對象可能被多個弱引用指針指向
weak原理實(shí)現(xiàn)步驟
weak 的實(shí)現(xiàn)原理可概括三步:
1、初始化時:runtime會調(diào)用objc_initWeak函數(shù)伸蚯,初始化一個新的weak指針指向?qū)ο蟮牡刂贰?/p>
2摩渺、添加引用時:objc_initWeak函數(shù)會調(diào)用 objc_storeWeak() 函數(shù), objc_storeWeak() 的作用是更新指針指向剂邮,創(chuàng)建對應(yīng)的弱引用表摇幻。
3、釋放時抗斤,調(diào)用clearDeallocating函數(shù)囚企。clearDeallocating函數(shù)首先根據(jù)對象地址獲取所有weak指針地址的數(shù)組,然后遍歷這個數(shù)組把其中的數(shù)據(jù)設(shè)為nil瑞眼,最后把這個entry從weak表中刪除,最后清理對象的記錄棵逊。
weak實(shí)現(xiàn)三步驟詳細(xì)過程:
1伤疙、初始化時:runtime會調(diào)用objc_initWeak函數(shù),objc_initWeak函數(shù)會初始化一個新的weak指針指向?qū)ο蟮牡刂贰?/p>
示例代碼:
NSObject *obj = [[NSObject alloc] init];
id __weak obj1 = obj;
當(dāng)我們初始化一個weak變量時辆影,runtime會調(diào)用 NSObject.mm 中的objc_initWeak函數(shù)徒像。
這個函數(shù)在Clang中的聲明如下:
id objc_initWeak(id *object, id value);
而對于 objc_initWeak() 方法的實(shí)現(xiàn)如下:
id objc_initWeak(id *location, id newObj) {
// 查看對象實(shí)例是否有效,無效對象直接導(dǎo)致指針釋放
if (!newObj) {
*location = nil;
return nil;
}
// 這里傳遞了三個 bool 數(shù)值
// 使用 template 進(jìn)行常量參數(shù)傳遞是為了優(yōu)化性能
return storeWeakfalse/*old*/, true/*new*/, true/*crash*/>
(location, (objc_object*)newObj);
}
這里先判斷了其指針指向的類對象是否有效,無效直接釋放返回蛙讥,不再往深層調(diào)用函數(shù)锯蛀。否則,object將通過bjc_storeWeak函數(shù)被注冊為一個指向value的__weak對象次慢。
注意:objc_initWeak函數(shù)有一個前提條件:就是object必須是一個沒有被注冊為__weak對象的有效指針旁涤。而value則可以是null,或者指向一個有效的對象迫像。
2劈愚、添加引用時:objc_initWeak函數(shù)會調(diào)用 objc_storeWeak() 函數(shù), objc_storeWeak() 的作用是更新指針指向闻妓,創(chuàng)建對應(yīng)的弱引用表菌羽。
objc_storeWeak的函數(shù)聲明如下:
id objc_storeWeak(id *location, id value);
objc_storeWeak() 的具體實(shí)現(xiàn),請參考weak弱引用實(shí)現(xiàn)的方式,這里的實(shí)現(xiàn)很復(fù)雜由缆,沒看懂注祖,沒看懂猾蒂。
3、釋放時是晨,調(diào)用clearDeallocating函數(shù)肚菠。clearDeallocating函數(shù)首先根據(jù)對象地址獲取所有weak指針地址的數(shù)組,然后遍歷這個數(shù)組把其中的數(shù)據(jù)設(shè)為nil署鸡,最后把這個entry從weak表中刪除案糙,最后清理對象的記錄。
當(dāng)weak引用指向的對象被釋放時靴庆,又是如何去處理weak指針的呢时捌?當(dāng)釋放對象時,其基本流程如下:
1炉抒、調(diào)用objc_release
2奢讨、因為對象的引用計數(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ì)過程如下:
a. 從weak表中獲取廢棄對象的地址為鍵值的記錄
b. 將包含在記錄中的所有附有 weak修飾符變量的地址野瘦,賦值為 nil
c. 將weak表中該記錄刪除
d. 從引用計數(shù)表中刪除廢棄對象的地址為鍵值的記錄
拓展補(bǔ)充
weak描沟,__unsafe_unretained, unowned 與 assign區(qū)別
__unsafe_unretained: 不會對對象進(jìn)行retain,當(dāng)對象銷毀時,會依然指向之前的內(nèi)存空間(野指針)
weak: 不會對對象進(jìn)行retain,當(dāng)對象銷毀時,會自動指向nil
assign: 實(shí)質(zhì)與__unsafe_unretained等同
unsafe_unretained也可以修飾代表簡單數(shù)據(jù)類型的property,weak也不能修飾用來代表簡單數(shù)據(jù)類型的property鞭光。
__unsafe_unretained 與 weak 比較吏廉,使用 weak 是有代價的,因為通過上面的原理可知惰许,__weak需要檢查對象是否已經(jīng)消亡席覆,而為了知道是否已經(jīng)消亡,自然也需要一些信息去跟蹤對象的使用情況汹买。也正因此佩伤,__unsafe_unretained 比 __weak快,所以當(dāng)明確知道對象的生命期時,選擇__unsafe_unretained 會有一些性能提升卦睹,這種性能提升是很微小的畦戒。但當(dāng)很清楚的情況下,__unsafe_unretained 也是安全的结序,自然能快一點(diǎn)是一點(diǎn)障斋。而當(dāng)情況不確定的時候,應(yīng)該優(yōu)先選用 __weak 。
unowned使用在Swift中垃环,也會分 weak 和 unowned邀层。unowned 的含義跟 __unsafe_unretained 差不多。假如很明確的知道對象的生命期遂庄,也可以選擇 unowned寥院。
致謝
參考博客:
weak 弱引用的實(shí)現(xiàn)方式
iOS 底層解析weak的實(shí)現(xiàn)原理(包含weak對象的初始化,引用涛目,釋放的分析)
個人簡書地址