你真的會用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的三種方式進(jìn)行討論,并給出原因和另外一種解決方案。

1. block中直接引用self

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

dispatch_block_t completionHandler = ^{

NSLog(@"%@", self);

}

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

當(dāng)block被self強(qiáng)引用,此時如果在block內(nèi)強(qiáng)引用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對他進(jìn)行了一次retain,當(dāng)doAnotherThing執(zhí)行完畢,strongself對他的引用計數(shù)減一,self被dealloc徹底銷毀。

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

我們改一下代碼,在1秒之后釋放self,在2秒之后執(zhí)行block(注意延時block中對于self的處理是weakself,防止延時block對self進(jìn)行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)

當(dāng)開始調(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進(jìn)行強(qiáng)引用。之后在block內(nèi)部創(chuàng)建一個對self進(jìn)行retain的變量strongself,strongself 作為局部變量強(qiáng)引用了 self 并且會在block執(zhí)行完畢的時候被自動銷毀,這樣既可以保證在block執(zhí)行期間 self 不會被外界干掉,同時也解決了retain cycle的問題。

總結(jié)

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

1. 使用self

當(dāng)self不持有、不間接持有block時,可以在block內(nèi)部直接引用self。

2.使用weakself

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

3.使用strong-weak dance

當(dāng)self持有或間接持有block,此時要使用strong-weak dance。

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

4. block中強(qiáng)引用self并且打破retain cycle

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

如果我們在block中直接使用self,并且在適當(dāng)?shù)臅r機(jī)打破這種循環(huán)(比如說在block執(zhí)行完成將這個block銷毀)也可以避免retain cycle,并且這種在block創(chuàng)建時就強(qiáng)引用的方式,在block被調(diào)用前 self 不會被釋放掉,可以彌補(bǔ)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)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,772評論 3 422
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,947評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,960評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,350評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,549評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,104評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,914評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,089評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,340評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,834評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,106評論 2 375

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