weak基本用法
weak是弱引用,用weak描述修飾或者所引用對象的計數(shù)器不會加一兑巾,并且會在引用的對象被釋放的時候自動被設(shè)置為nil,大大避免了野指針訪問壞內(nèi)存引起崩潰的情況忠荞,另外weak還可以用于解決循環(huán)引用蒋歌。
weak原理概括
weak表其實是一個hash(哈希)表,Key是所指對象的地址委煤,Value是weak指針的地址數(shù)組堂油。weak的底層實現(xiàn)的原理是什么?
Runtime維護(hù)了一個weak表素标,用于存儲指向某個對象的所有weak指針称诗。weak表其實是一個hash表,Key是所指對象的地址头遭,value是weak指針的地址(這個地址的值是所指對象指針的地址)數(shù)組寓免。
為什么value是數(shù)組?因為一個對象可能被多個弱引用指針指向
weak原理實現(xiàn)步驟
weak 的實現(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實現(xiàn)三步驟詳細(xì)過程:
- 1俺孙、初始化時:runtime會調(diào)用objc_initWeak函數(shù)辣卒,objc_initWeak函數(shù)會初始化一個新的weak指針指向?qū)ο蟮牡刂贰?br> 示例代碼:
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() 方法的實現(xiàn)如下:
// 查看對象實例是否有效,無效對象直接導(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() 的具體實現(xiàn),請參考weak弱引用實現(xiàn)的方式,這里的實現(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: 實質(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 也是安全的缕棵,自然能快一點是一點孵班。而當(dāng)情況不確定的時候,應(yīng)該優(yōu)先選用 __weak 招驴。
unowned使用在Swift中篙程,也會分 weak 和 unowned。unowned 的含義跟 __unsafe_unretained 差不多别厘。假如很明確的知道對象的生命期虱饿,也可以選擇 unowned。