ios中的block與weakSelf

https://blog.csdn.net/xgb742951920/article/details/69258372

結(jié)論:

1.不成環(huán),并且想讓block代碼什么情況下都執(zhí)行:兩種方式:A全部使用self就行;B外面定義weak,block里面用strong,也行,多次一舉。


2.不成環(huán),并且想讓block代碼在當前VC釋放的情況下不執(zhí)行:兩種方式:A外面定義weak,里面使用weak,然后注意nil可能會crash的地方(加判斷);B外面定義weak,block里面使用strong(或者直接使用self),自己加if判斷,否是在當前頁面,不在當前頁面不執(zhí)行。


3.成環(huán),想讓block代碼無論如何都執(zhí)行:必用weak。block里面用strong。


4.成環(huán),想讓block代碼在當前VC釋放的情況下不執(zhí)行:兩種方式:A必用weak,block里面用strong,則自己加if判斷不在當前頁面就不執(zhí)行;B,block里面使用weak,注意nil可能導致crash的地方。


重點注意:弄清楚block到底被誰持有,例如UIView的animateWithDuration方法,大部分時候?qū)懙膁ispatch,傳參等,都沒有成環(huán),是不用定義weak和strong的。



我們知道,在使用 block 的時候,為了避免產(chǎn)生循環(huán)引用,通常需要使用 weakSelf 與 strongSelf,寫下面這樣的代碼:

__weak typeof(self) weakSelf = self;[self doSomeBlockJob:^{? ? __strong typeof(weakSelf) strongSelf = weakSelf;if(strongSelf) {...}}];

那么請問:什么時候在 block 里面用 self,不需要使用 weak self?

答案

當 block 本身不被 self 持有,而被別的對象持有,同時不產(chǎn)生循環(huán)引用的時候,就不需要使用 weak self 了。最常見的代碼就是 UIView 的動畫代碼,我們在使用 UIView 的 animateWithDuration:animations 方法 做動畫的時候,并不需要使用 weak self,因為引用持有關(guān)系是:

UIView 的某個負責動畫的對象持有了 block?

block 持有了 self?

因為 self 并不持有 block,所以就沒有循環(huán)引用產(chǎn)生,因為就不需要使用 weak self 了。

[UIView animateWithDuration:0.2 animations:^{

? ? self.alpha = 1;

}];

當動畫結(jié)束時,UIView 會結(jié)束持有這個 block,如果沒有別的對象持有 block 的話,block 對象就會釋放掉,從而 block 會釋放掉對于 self 的持有。整個內(nèi)存引用關(guān)系被解除。

思考題

如果覺得上面的問題太簡單,可以想想下面兩個題目:

為什么 block 里面還需要寫一個 strong self,如果不寫會怎么樣??

有沒有這樣一個需求場景,block會產(chǎn)生循環(huán)引用,但是業(yè)務又需要你不能使用 weak self? 如果有,請舉一個例子并且解釋這種情況下如何解決循環(huán)引用問題。

2

繼續(xù)回答昨天的問題第二問。

我們知道,在使用 block 的時候,為了避免產(chǎn)生循環(huán)引用,通常需要使用 weakSelf 與 strongSelf,寫下面這樣的代碼:

__weak typeof(self) weakSelf = self;[self doSomeBackgroundJob:^{? ? __strong typeof(weakSelf) strongSelf = weakSelf;if(strongSelf) {...}}];

那么請問:為什么 block 里面還需要寫一個 strong self,如果不寫會怎么樣?

答案

在 block 中先寫一個 strong self,其實是為了避免在 block 的執(zhí)行過程中,突然出現(xiàn) self 被釋放的尷尬情況。通常情況下,如果不這么做的話,還是很容易出現(xiàn)一些奇怪的邏輯,甚至閃退。

我們以 AFNetworking 中 AFNetworkReachabilityManager.m 的一段代碼舉例:

__weak__typeof(self)weakSelf =self;AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {? ? __strong__typeof(weakSelf)strongSelf = weakSelf;? ? strongSelf.networkReachabilityStatus= status;if(strongSelf.networkReachabilityStatusBlock) {? ? ? ? strongSelf.networkReachabilityStatusBlock(status);? ? }};

如果沒有 strongSelf 的那行代碼,那么后面的每一行代碼執(zhí)行時,self 都可能被釋放掉了,這樣很可能造成邏輯異常。

特別是當我們正在執(zhí)行 strongSelf.networkReachabilityStatusBlock(status); 這個 block 閉包時,如果這個 block 執(zhí)行到一半時 self 釋放,那么多半情況下會 Crash。

這里有一篇文章詳細解釋了這個問題:https://dhoerl.wordpress.com/2013/04/23/i-finally-figured-out-weakself-and-strongself/

另外,還有讀者提了兩個有意思的問題,大家可以思考一下:

提問:“數(shù)組” 和 “字典” 的 enumeratXXXUsingBlock: 是否要使用 weakSelf 和 strongSelf 呢?

提問:block 里 strong self 后,block 不是也會持有 self 嗎?而 self 又持有 block ,那不是又循環(huán)引用了?

3

有沒有這樣一個需求場景,block 會產(chǎn)生循環(huán)引用,但是業(yè)務又需要你不能使用 weak self? 如果有,請舉一個例子并且解釋這種情況下如何解決循環(huán)引用問題。

答案

需要不使用 weak self 的場景是:你需要構(gòu)造一個循環(huán)引用,以便保證引用雙方都存在。比如你有一個后臺的任務,希望任務執(zhí)行完后,通知另外一個實例。在我們開源的 YTKNetwork 網(wǎng)絡(luò)庫的源碼中,就有這樣的場景。

在 YTKNetwork 庫中,我們的每一個網(wǎng)絡(luò)請求 API 會持有回調(diào)的 block,回調(diào)的 block 會持有 self,而如果 self 也持有網(wǎng)絡(luò)請求 API 的話,我們就構(gòu)造了一個循環(huán)引用。雖然我們構(gòu)造出了循環(huán)引用,但是因為在網(wǎng)絡(luò)請求結(jié)束時,網(wǎng)絡(luò)請求 API 會主動釋放對 block 的持有,因此,整個循環(huán)鏈條被解開,循環(huán)引用就被打破了,所以不會有內(nèi)存泄漏問題。代碼其實很簡單,如下所示:

- (void)clearCompletionBlock {//nilout tobreakthe retain cycle.self.successCompletionBlock =nil;self.failureCompletionBlock =nil;}

總結(jié)來說,解決循環(huán)引用問題主要有兩個辦法:

第一個辦法是「事前避免」,我們在會產(chǎn)生循環(huán)引用的地方使用 weak 弱引用,以避免產(chǎn)生循環(huán)引用。?

第二個辦法是「事后補救」,我們明確知道會存在循環(huán)引用,但是我們在合理的位置主動斷開環(huán)中的一個引用,使得對象得以回收。?

思考題

下期的問題是:weak 變量在引用計數(shù)為 0 時,會被自動設(shè)置成 nil,這個特性是如何實現(xiàn)的?

4

weak 變量在引用計數(shù)為0時,會被自動設(shè)置成 nil,這個特性是如何實現(xiàn)的?

答案

在 Friday QA 上,有一期專門介紹 weak 的實現(xiàn)原理。https://mikeash.com/pyblog/friday-qa-2010-07-16-zeroing-weak-references-in-objective-c.html

《Objective-C高級編程》一書中也介紹了相關(guān)的內(nèi)容。

簡單來說,系統(tǒng)有一個全局的 CFMutableDictionary 實例,來保存每個對象的 weak 指針列表,因為每個對象可能有多個 weak 指針,所以這個實例的值是 CFMutableSet 類型。

剩下我們要做的,就是在引用計數(shù)變成 0 的時候,去這個全局的字典里面,找到所有的 weak 指針,將其值設(shè)置成 nil。如何做到這一點呢?Friday QA 上介紹了一種類似 KVO 實現(xiàn)的方式。當對象存在 weak 指針時,我們可以將這個實例指向一個新創(chuàng)建的子類,然后修改這個子類的 release 方法,在 release 方法中,去從全局的 CFMutableDictionary 字典中找到所有的 weak 對象,并且設(shè)置成 nil。我摘抄了 Friday QA 上的實現(xiàn)的核心代碼,如下:

Classsubclass = objc_allocateClassPair(class, newNameC,0);Methodrelease=class_getInstanceMethod(class, @selector(release));Methoddealloc=class_getInstanceMethod(class, @selector(dealloc));class_addMethod(subclass, @selector(release), (IMP)CustomSubclassRelease, method_getTypeEncoding(release));class_addMethod(subclass, @selector(dealloc), (IMP)CustomSubclassDealloc, method_getTypeEncoding(dealloc));objc_registerClassPair(subclass);

當然,這并不代表蘋果官方是這么實現(xiàn)的,因為蘋果的這部分代碼并沒有開源?!禣bjective-C高級編程》一書中介紹了 GNUStep 項目中的開源代碼,思想也是類似的。所以我認為雖然實現(xiàn)細節(jié)會有差異,但是大致的實現(xiàn)思路應該差別不大。

全文完。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

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