iOS中block的詳解weakSelf萍桌、strongSelf <轉(zhuǎn)自唐巧>

1

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

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

那么請問:什么時(shí)候在 block 里面用 self雏搂,不需要使用 weak self藕施?

答案

當(dāng) block 本身不被 self 持有,而被別的對象持有凸郑,同時(shí)不產(chǎn)生循環(huán)引用的時(shí)候裳食,就不需要使用 weak self 了。最常見的代碼就是 UIView 的動(dòng)畫代碼芙沥,我們在使用 UIView 的 animateWithDuration:animations 方法 做動(dòng)畫的時(shí)候诲祸,并不需要使用 weak self,因?yàn)橐贸钟嘘P(guān)系是:

UIView 的某個(gè)負(fù)責(zé)動(dòng)畫的對象持有了 block?

block 持有了 self?

因?yàn)?self 并不持有 block而昨,所以就沒有循環(huán)引用產(chǎn)生救氯,因?yàn)榫筒恍枰褂?weak self 了。

[UIView animateWithDuration:0.2 animations:^{

? ? self.alpha = 1;

}];

當(dāng)動(dòng)畫結(jié)束時(shí)歌憨,UIView 會(huì)結(jié)束持有這個(gè) block着憨,如果沒有別的對象持有 block 的話,block 對象就會(huì)釋放掉务嫡,從而 block 會(huì)釋放掉對于 self 的持有甲抖。整個(gè)內(nèi)存引用關(guān)系被解除。

思考題

如果覺得上面的問題太簡單心铃,可以想想下面兩個(gè)題目:

為什么 block 里面還需要寫一個(gè) strong self惧眠,如果不寫會(huì)怎么樣??

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

2

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

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

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

1

2

3

那么請問:為什么 block 里面還需要寫一個(gè) strong self档押,如果不寫會(huì)怎么樣澳盐?

答案

在 block 中先寫一個(gè) strong self祈纯,其實(shí)是為了避免在 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í)行時(shí)爽撒,self 都可能被釋放掉了入蛆,這樣很可能造成邏輯異常。

特別是當(dāng)我們正在執(zhí)行 strongSelf.networkReachabilityStatusBlock(status); 這個(gè) block 閉包時(shí)硕勿,如果這個(gè) block 執(zhí)行到一半時(shí) self 釋放哨毁,那么多半情況下會(huì) Crash涯曲。

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

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

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

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

3

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

答案

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

在 YTKNetwork 庫中捶箱,我們的每一個(gè)網(wǎng)絡(luò)請求 API 會(huì)持有回調(diào)的 block,回調(diào)的 block 會(huì)持有 self动漾,而如果 self 也持有網(wǎng)絡(luò)請求 API 的話丁屎,我們就構(gòu)造了一個(gè)循環(huán)引用。雖然我們構(gòu)造出了循環(huán)引用旱眯,但是因?yàn)樵诰W(wǎng)絡(luò)請求結(jié)束時(shí)晨川,網(wǎng)絡(luò)請求 API 會(huì)主動(dòng)釋放對 block 的持有证九,因此,整個(gè)循環(huán)鏈條被解開共虑,循環(huán)引用就被打破了愧怜,所以不會(huì)有內(nèi)存泄漏問題。代碼其實(shí)很簡單看蚜,如下所示:

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

1


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

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

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

思考題

下期的問題是:weak 變量在引用計(jì)數(shù)為 0 時(shí)竭钝,會(huì)被自動(dòng)設(shè)置成 nil梨撞,這個(gè)特性是如何實(shí)現(xiàn)的?

4

weak 變量在引用計(jì)數(shù)為0時(shí)香罐,會(huì)被自動(dòng)設(shè)置成 nil卧波,這個(gè)特性是如何實(shí)現(xiàn)的?

答案

在 Friday QA 上庇茫,有一期專門介紹 weak 的實(shí)現(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)有一個(gè)全局的 CFMutableDictionary 實(shí)例查坪,來保存每個(gè)對象的 weak 指針列表,因?yàn)槊總€(gè)對象可能有多個(gè) weak 指針宁炫,所以這個(gè)實(shí)例的值是 CFMutableSet 類型偿曙。

剩下我們要做的,就是在引用計(jì)數(shù)變成 0 的時(shí)候羔巢,去這個(gè)全局的字典里面望忆,找到所有的 weak 指針,將其值設(shè)置成 nil竿秆。如何做到這一點(diǎn)呢炭臭?Friday QA 上介紹了一種類似 KVO 實(shí)現(xiàn)的方式。當(dāng)對象存在 weak 指針時(shí)袍辞,我們可以將這個(gè)實(shí)例指向一個(gè)新創(chuàng)建的子類鞋仍,然后修改這個(gè)子類的 release 方法,在 release 方法中搅吁,去從全局的 CFMutableDictionary 字典中找到所有的 weak 對象威创,并且設(shè)置成 nil落午。我摘抄了 Friday QA 上的實(shí)現(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);

1

2


當(dāng)然肚豺,這并不代表蘋果官方是這么實(shí)現(xiàn)的溃斋,因?yàn)樘O果的這部分代碼并沒有開源∥辏《Objective-C高級編程》一書中介紹了 GNUStep 項(xiàng)目中的開源代碼梗劫,思想也是類似的。所以我認(rèn)為雖然實(shí)現(xiàn)細(xì)節(jié)會(huì)有差異截碴,但是大致的實(shí)現(xiàn)思路應(yīng)該差別不大梳侨。

全文完。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末日丹,一起剝皮案震驚了整個(gè)濱河市走哺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哲虾,老刑警劉巖丙躏,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異束凑,居然都是意外死亡晒旅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門汪诉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來废恋,“玉大人,你說我怎么就攤上這事摩瞎。” “怎么了孝常?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵旗们,是天一觀的道長。 經(jīng)常有香客問我构灸,道長上渴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任喜颁,我火速辦了婚禮稠氮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘半开。我一直安慰自己隔披,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布寂拆。 她就那樣靜靜地躺著奢米,像睡著了一般抓韩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鬓长,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天谒拴,我揣著相機(jī)與錄音,去河邊找鬼涉波。 笑死英上,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的啤覆。 我是一名探鬼主播苍日,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼城侧!你這毒婦竟也來了易遣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤嫌佑,失蹤者是張志新(化名)和其女友劉穎豆茫,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屋摇,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡揩魂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了炮温。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片火脉。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖柒啤,靈堂內(nèi)的尸體忽然破棺而出倦挂,到底是詐尸還是另有隱情,我是刑警寧澤担巩,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布方援,位于F島的核電站,受9級特大地震影響涛癌,放射性物質(zhì)發(fā)生泄漏犯戏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一拳话、第九天 我趴在偏房一處隱蔽的房頂上張望先匪。 院中可真熱鬧,春花似錦弃衍、人聲如沸呀非。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姜钳。三九已至坦冠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間哥桥,已是汗流浹背辙浑。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拟糕,地道東北人判呕。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像送滞,于是被迫代替她去往敵國和親侠草。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容