平時開發(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