你真的會(huì)用strong-weak dance嗎?

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

__weak typeof(self) weakSelf = self;

self.myBlock =? ^{

__strong typeof(self) strongSelf = weakSelf;

[strongSelf doSomething];

[strongSelf doSomethingElse];

};

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

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

1. block中直接引用self

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

dispatch_block_t completionHandler = ^{

NSLog(@"%@", self);

}

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

當(dāng)block被self強(qiáng)引用,此時(shí)如果在block內(nèi)強(qiáng)引用self將會(huì)導(dǎo)致retain cycle。所以我們就想到了在block外部創(chuàng)建一個(gè)weakself,然后block在創(chuàng)建的時(shí)候捕獲到的是weakself,這樣就不會(huì)導(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í)行的,看一個(gè)例子:

下面的例子展示的是,在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是非常危險(xiǎn)的,下面就要談?wù)勎覀冏钍煜さ膕trong-weak dance了。

3.strong-weak dance

對(duì)比第二種方案我們看一下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();

}

此時(shí)看打印日志:

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,因?yàn)閎lock內(nèi)部的strongself對(duì)他進(jìn)行了一次retain,當(dāng)doAnotherThing執(zhí)行完畢,strongself對(duì)他的引用計(jì)數(shù)減一,self被dealloc徹底銷毀。

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

我們改一下代碼,在1秒之后釋放self,在2秒之后執(zhí)行block(注意延時(shí)block中對(duì)于self的處理是weakself,防止延時(shí)block對(duì)self進(jìn)行retain影響驗(yàn)證結(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的時(shí)候報(bào)錯(cuò)了,self這時(shí)已經(jīng)被dealloc掉。strong-weak dance并沒有解決這種問題??吹竭@心是不是涼了半截?真相就是如此,我們平時(shí)一直使用的strong-weak dance也只能解決block得到調(diào)用之后self不被釋放的問題。

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

總結(jié)

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

1. 使用self

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

2.使用weakself

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

3.使用strong-weak dance

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

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

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

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

如果我們?cè)赽lock中直接使用self,并且在適當(dāng)?shù)臅r(shí)機(jī)打破這種循環(huán)(比如說在block執(zhí)行完成將這個(gè)block銷毀)也可以避免retain cycle,并且這種在block創(chuàng)建時(shí)就強(qiáng)引用的方式,在block被調(diào)用前 self 不會(huì)被釋放掉,可以彌補(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)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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