你真的會用strong-weak dance嗎?

平時開發(fā)中我們遇到block里面引用self的情況段磨,大部分都是這樣處理的

__weak typeof(self) weakSelf = self;

self.myBlock =? ^{

__strong typeof(self) strongSelf = weakSelf;

[strongSelf doSomething];

[strongSelf doSomethingElse];

};

轉(zhuǎn)載請注明出處:來自LeonLei的博客http://www.gaoshilei.com

我們習(xí)慣了這樣用取逾,貌似這樣用了之后可以解決循環(huán)引用的問題,而且可以保證block執(zhí)行之前self不會被釋放掉苹支?真相總是殘酷的砾隅,然而事實并非如此!下面將會對block中引用self的三種方式進行討論晴埂,并給出原因和另外一種解決方案精耐。

1. block中直接引用self

這種情況使用是block被沒有被self強引用,因此這樣不會導(dǎo)致retain cycle。

dispatch_block_t completionHandler = ^{

NSLog(@"%@", self);

}

2.在block外部創(chuàng)建weakself變量专执,在block中引用weakself

當block被self強引用,此時如果在block內(nèi)強引用self將會導(dǎo)致retain cycle。所以我們就想到了在block外部創(chuàng)建一個weakself躬审,然后block在創(chuàng)建的時候捕獲到的是weakself石挂,這樣就不會導(dǎo)致retain cycle富岳。

__weak typeof(self) weakSelf = self;

dispatch_block_t block =? ^{

[weakSelf doSomething];

[weakSelf doSomethingElse];

};

但是要注意的是block捕獲的是weakself變量,如果在執(zhí)行doSomething的過程中self被釋放掉,由于是弱引用弦蹂,weakself也將置空,下面的doSomethingElse是無法得到執(zhí)行的,看一個例子:

下面的例子展示的是优幸,在block調(diào)用之后的1秒后釋放self,在block中調(diào)用doSomething,2秒之后再調(diào)用doAnotherThing昼浦,意味著調(diào)用doAnotherThing之前self已經(jīng)被釋放了

//viewController.m

- (void)viewDidLoad {

[super viewDidLoad];

self.sself = [strongweakself new];

[self.sself test];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

NSLog(@"self.block被釋放!");

self.sself = nil;

});

}

//strongweakself.m

-(void)test

{

self.myobject = [TestObject new];

__weak typeof(self) __weakself = self;

[self.myobject setWeakblock:^{

NSLog(@"調(diào)用block!");

[__weakself doSomething];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

[__weakself doAnotherThing];

});

}];

self.myobject.weakblock();

}

-(void)doSomething

{

NSLog(@"%s",__func__);

}

-(void)doAnotherThing

{

NSLog(@"%s",__func__);

}

-(void)dealloc{

NSLog(@"%s",__func__);

}

從打印日志可以看出色洞,block執(zhí)行大約1秒之后self被dealloc荠察,doAnotherThing并沒有得到調(diào)用

2017-01-16 14:31:13.834 strong-weak dance[11366:4727954] 調(diào)用block!

2017-01-16 14:31:13.836 strong-weak dance[11366:4727954] -[strongweakself doSomething]

2017-01-16 14:31:14.893 strong-weak dance[11366:4727954] self.block被釋放!

2017-01-16 14:31:14.893 strong-weak dance[11366:4727954] -[strongweakself dealloc]

所以只使用weakself馋吗,在self被釋放之后灼卢,weakself由于self的釋放已經(jīng)為空,后面的self都將失效,所以在block中這樣引用self是非常危險的檩互,下面就要談?wù)勎覀冏钍煜さ膕trong-weak dance了零院。

3.strong-weak dance

對比第二種方案我們看一下doAnotherThing是否可以得到調(diào)用,稍微改一下代碼打洼,還是在block執(zhí)行1秒后釋放self僻弹,我們看看后面的self引用是否有效

-(void)test

{

self.myobject = [TestObject new];

__weak typeof(self) __weakself = self;

[self.myobject setWeakblock:^{

NSLog(@"調(diào)用block!");

__strong typeof(self) __strongself= __weakself;

[__strongself doSomething];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

[__strongself doAnotherThing];

});

}];

self.myobject.weakblock();

}

此時看打印日志:

2017-01-16 14:36:39.039 strong-weak dance[11374:4728878] 調(diào)用block!

2017-01-16 14:36:39.039 strong-weak dance[11374:4728878] -[strongweakself doSomething]

2017-01-16 14:36:40.110 strong-weak dance[11374:4728878] self.block被釋放!

2017-01-16 14:36:41.213 strong-weak dance[11374:4728878] -[strongweakself doAnotherThing]

2017-01-16 14:36:41.213 strong-weak dance[11374:4728878] -[strongweakself dealloc]

雖然self被釋放掉了卸耘,但是并沒有dealloc,因為block內(nèi)部的strongself對他進行了一次retain讽坏,當doAnotherThing執(zhí)行完畢战虏,strongself對他的引用計數(shù)減一手趣,self被dealloc徹底銷毀。

那么問題來了!strong-weak dance能不能解決block執(zhí)行前右莱,self被釋放的問題晨抡?下面繼續(xù)驗證

我們改一下代碼,在1秒之后釋放self,在2秒之后執(zhí)行block(注意延時block中對于self的處理是weakself窖剑,防止延時block對self進行retain影響驗證結(jié)果)

//viewController.m

- (void)viewDidLoad {

[super viewDidLoad];

self.sself = [strongweakself new];

[self.sself test];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

NSLog(@"self.block被釋放!");

self.sself = nil;

});

}

//strongweakself.m

-(void)test

{

self.myobject = [TestObject new];

__weak typeof(self) __weakself = self;

[self.myobject setWeakblock:^{

NSLog(@"調(diào)用block!");

__strong typeof(self) __strongself= __weakself;

[__strongself doSomething];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

[__strongself doAnotherThing];

});

}];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

NSLog(@"%@",__weakself);

__weakself.myobject.weakblock();

});

}

看一下日志:

2017-01-16 14:44:26.314 strong-weak dance[11395:4730727] self.block被釋放!

2017-01-16 14:44:26.314 strong-weak dance[11395:4730727] -[strongweakself dealloc]

2017-01-16 14:44:27.372 strong-weak dance[11395:4730727] (null)

當開始調(diào)用block的時候報錯了,self這時已經(jīng)被dealloc掉。strong-weak dance并沒有解決這種問題。看到這心是不是涼了半截?真相就是如此,我們平時一直使用的strong-weak dance也只能解決block得到調(diào)用之后self不被釋放的問題曼氛。

這是我們最常用的是一種方案聊浅,因為block創(chuàng)建時捕獲的是weakself,所以block執(zhí)行之前不能夠控制self的生命周期,所以這樣不會導(dǎo)致整個block對self進行強引用。之后在block內(nèi)部創(chuàng)建一個對self進行retain的變量strongself秉溉,strongself 作為局部變量強引用了 self 并且會在block執(zhí)行完畢的時候被自動銷毀甲喝,這樣既可以保證在block執(zhí)行期間 self 不會被外界干掉直撤,同時也解決了retain cycle的問題。

總結(jié)

通過上面幾個小栗子可以看出來:strong-weak dance確實是比較好的解決方案沈贝,但是也不是萬能的娩梨,他不能解決block調(diào)用之前self被釋放的問題纽什,下面將block中引用self分為4中場景:

1. 使用self

當self不持有或听、不間接持有block時粱腻,可以在block內(nèi)部直接引用self遇革。

2.使用weakself

當self持有或間接持有block,可以通過在外部創(chuàng)建self的弱引用weakself然后捕獲到block內(nèi)部進行使用冰更,但是這樣使用存在一定風險,一般也不推薦使用。

3.使用strong-weak dance

當self持有或間接持有block,此時要使用strong-weak dance根吁。

這種方法也不是萬能的圣蝎,在block被執(zhí)行前关面,block對self依然只是弱引用,進入block里面才會retain一次压真,保證在block執(zhí)行期間self都不會被釋放掉。

4. block中強引用self并且打破retain cycle

不管是weakself還是strong-weak dance,目的都是避免retain cycle,strong-weak dance的本質(zhì)也是在block中搞了一個局部變量來打破這種循環(huán)引用的涝影;

如果我們在block中直接使用self猿涨,并且在適當?shù)臅r機打破這種循環(huán)(比如說在block執(zhí)行完成將這個block銷毀)也可以避免retain cycle,并且這種在block創(chuàng)建時就強引用的方式膨桥,在block被調(diào)用前 self 不會被釋放掉蕴掏,可以彌補strong-weak dance的不足定拟。

關(guān)于本文的內(nèi)容可能存在不足的地方,歡迎大家指正祈远!

參考資料

http://albertodebortoli.com/blog/2013/08/03/objective-c-blocks-caveat

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末严就,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卓鹿,老刑警劉巖肥隆,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兼蕊,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機盹愚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門悦即,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宗挥,“玉大人,你說我怎么就攤上這事淌山≡棠桑” “怎么了塞椎?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵澄暮,是天一觀的道長窃蹋。 經(jīng)常有香客問我匈辱,道長浅碾,這世上最難降的妖魔是什么垂谢? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任焚虱,我火速辦了婚禮民鼓,結(jié)果婚禮上荐开,老公的妹妹穿的比我還像新娘。我一直安慰自己膨处,他們只是感情好越平,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布频蛔。 她就那樣靜靜地躺著,像睡著了一般秦叛。 火紅的嫁衣襯著肌膚如雪晦溪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天挣跋,我揣著相機與錄音三圆,去河邊找鬼。 笑死避咆,一個胖子當著我的面吹牛舟肉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播查库,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼路媚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了樊销?” 一聲冷哼從身側(cè)響起整慎,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎围苫,沒想到半個月后裤园,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡剂府,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年拧揽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腺占。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡淤袜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出衰伯,到底是詐尸還是另有隱情饮怯,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布嚎研,位于F島的核電站,受9級特大地震影響库倘,放射性物質(zhì)發(fā)生泄漏临扮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一教翩、第九天 我趴在偏房一處隱蔽的房頂上張望杆勇。 院中可真熱鬧,春花似錦饱亿、人聲如沸蚜退。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钻注。三九已至蚂且,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間幅恋,已是汗流浹背杏死。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留捆交,地道東北人淑翼。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像品追,于是被迫代替她去往敵國和親玄括。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

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