,經常說在 block 里面很容易導致循環(huán)引用(retain cycle),可是為什么會導致循環(huán)引用,卻不是特別理解,最近研究了,也算是做一個總結.
常見的循環(huán)引用
看看這個類
代碼1
@interface TestClass : NSObject
@property (nonatomic,copy)void (^block)();
-(void)test;
@end
有一個屬性, block,一個方法 test
來看看類實現(xiàn)
代碼2
@implementation TestClass
-(void)dealloc{
NSLog(@"dealloc");
}
-(void)test{
self.block=^{
NSLog(@"%@",self);
};
}
@end
可以看到, test
方法中有一個明顯的在 self.block
中調用self
的過程.來看看會不會導致循環(huán)引用.
接下來,隨便在其他類中來調用一下這個類
代碼3
TestClass* test=[TestClass new];
[test test];
運行試試
2016-01-29 10:55:14.035 test[16318:1121947] <TestClass: 0x7fd3eb52b250>**
沒有輸出 "dealloc",說明 test對象沒有被釋放,怎么辦?
很簡單就會想到使用弱引用,
來看看使用弱引用的代碼
代碼4
-(void)test{
__weak typeof(self) weakself=self;
self.block=^{
NSLog(@"%@",weakself);
};
self.block();
}
輸出
2016-01-29 10:58:01.899 test[16360:1124161] <TestClass: 0x7f9d61c4c240>
2016-01-29 10:58:01.899 test[16360:1124161] dealloc
可以看到, self
被成功釋放了,沒有被循環(huán)引用.
為什么?
首先, block
會自動捕獲block
內部使用的變量,并對其強引用.具體過程可以參考 Objective-C高級編程.
在本例中代碼2, block
內部有 self
,因為self
本身是一個強引用,所以block
會再給 self
加上一個強引用.
而block
本身是self
的一個屬性,self
也強持有block
.
這就出現(xiàn)一個問題, self
強持有block
,block
強持有self
,當self
想要釋放的時候,要看一下有沒有自己強引用,而 block
剛好強持有自己,所以,暫時沒辦法釋放.block
想要釋放的時候,卻發(fā)現(xiàn) self
強持有自己,所以也得不到釋放,相互糾纏.
而為何代碼4 又可以呢?
代碼4中, block
內部有一個 weakself
,weakself
本身是一個弱引用,由于對弱引用無法強持有,所以, block 并沒有強持有self
,當 self 想要釋放的時候,已經沒有其他強引用了,就可以釋放.self
被釋放,就沒有變量強持有block
,block
也會釋放.這樣,循環(huán)引用就不存在了.
引申
既然只要不相互持有就可以,那么我們手動的去掉強引用還會不會發(fā)生循環(huán)引用呢?
看看這個代碼.
代碼5
-(void)test{
self.block=^{
NSLog(@"%@",self);
};
self.block();
self.block=nil;
}
這里在執(zhí)行完畢后,手動去掉了 block 的強引用.由于打破了相互引用,自然也不會有循環(huán)引用.
看看輸出
2016-01-29 11:25:25.238 test[16439:1133368] <TestClass: 0x7fb381511b80>
2016-01-29 11:25:25.239 test[16439:1133368] dealloc
果然如此,不過這種方法畢竟很不安全,要是block
是需要在一段時間后調用的,如果你馬上就釋放了,那么 block 中代碼就不能執(zhí)行.又或者,如果你忘記手動釋放,也會造成很大麻煩,相對之下,使用 weakself
就好多了.
另外,在使用weakself
的時候,可以和strongself
一起使用,可以參考我的文章為什么要用 strongSelf