你踩過的坑里盡是前人的腳印颂斜。---------前言
在開發(fā)過程中我們會遇到一些循環(huán)引用的問題惠拭,像循環(huán)引用Block是我們最常見的循環(huán)引用問題删性,但是有一些循環(huán)引用是很隱蔽的,稍有不慎就會導致內存泄漏油坝,比如GCD&NSNotification嫉戚。今天我們就踩在前人的腳印上來分析循環(huán)引用這個老生常談的話題。
一澈圈、Block
我們舉一個最簡單的例子:
[self.teacher requestData:^(NSData *data) {
self.name = @"case";
}];
這種情況是最常見的循環(huán)引用導致的內存泄漏的情況彬檀,此時,self強引用了teacher瞬女,而teacher又強引用了一個Block窍帝,該Block回調時又強引用了self,從而導致了循環(huán)引用诽偷,self無法釋放坤学。
self->teacher->block->self
解決辦法
此時我們通過__weak弱引用self,在Block里面調用weakself從而打破循環(huán)报慕。
__weak typeof(self) weakSelf = self;
[self.teacher requestData:^(NSData *data) {
//__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf.name = @"case";
}];
注意:我們在使用為什么要在Block里面添加__strong呢深浮?在什么情況下使用__strong呢?會造成什么影響呢眠冈?下面我們就根據(jù)具體的場景來分析:
場景1:block非異步執(zhí)行
在調用requestData:block時就已經(jīng)執(zhí)行飞苇,因為在同步情況下,self是不會在Block執(zhí)行時被釋放掉的了蜗顽,此時的strongself變量是可以省略的布卡。
場景2:block異步執(zhí)行
//此方法在A頁面,觸發(fā)之后立刻進入到B頁面雇盖。
self.block = ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:5];
[weakSelf print];
});
};
- (void)print
{
NSLog(@"---->print");
}
在此種場景下忿等,Block內部的方法不會執(zhí)行,但是為什么呢刊懈?原因就是self在Block執(zhí)行完畢之前就釋放掉了这弧,Block實體釋放了娃闲,self就指向nil虚汛,weakSelf也會被置nil,這就相當于想一個nil發(fā)送消息皇帮。__strong它的主要作用就是應對異步執(zhí)行的Block卷哩。處理如下:
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:5];
[strongSelf print];
});
};
引申出的問題:
到這里肯定有同學有疑問了,再使用__strong不是又強引用了嗎属拾,這不又成了循環(huán)引用了将谊?實際上并非如此冷溶,__strong修飾的strongSelf是Block內部的一個局部變量,也就是說作用域僅在Block內部尊浓,一旦跳出作用域逞频,那么我們強制產(chǎn)生的臨時“循環(huán)引用”就會被打破,所以也就不會有循環(huán)引用的情況了栋齿。
二苗胀、GCD
GCD使用參數(shù)包含self,且Block中也包含self瓦堵,那么此時就要考慮循環(huán)引用的問題了:
@property (nonatomic,retain) dispatch_queue_t operationsQueue;
//tips:在iOS6.0之前也就是MRC時代是使用assign基协。
__weak __typeof__(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
__typeof__(self) strongSelf = weakSelf;
[strongSelf doSomething];
[strongSelf doSomethingElse];
} );
三、NSNotification
@property (nonatomic, strong) id observer; //持有注冊通知后返回的對象
__weak __typeof__(self) weakSelf = self;
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"testKey"
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
__typeof__(self) strongSelf = weakSelf;
[strongSelf dismissModalViewControllerAnimated:YES];
}];
self --> _observer --> block --> self 顯然這也是一個循環(huán)引用菇用,所以也要進行弱引用處理澜驮。
tips: Facebook 開源的一個檢測循環(huán)引用工具 FBRetainCycleDetector 。
四惋鸥、不會造成循環(huán)引用的情況
1杂穷、UIView動畫
[UIView animateWithDuration:duration animations:^{ [self.superview layoutIfNeeded]; }];
2、NSNotification
[[NSNotificationCenter defaultCenter] addObserverForName:@"someNotification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * notification) {
self.someProperty = xyz; }];
3揩慕、GCD
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.someProperty = xyz; }];
以上三種情況都是單向“強引用”亭畜,只是Block持有self,但self并沒有持有block迎卤,所以不用考慮“循環(huán)引用”的問題拴鸵。
參考資料如下
Block循環(huán)引用分析:
https://blog.csdn.net/lizitao/article/details/54845974
http://www.cocoachina.com/ios/20170122/18601.html
NSNotification循環(huán)引用分析:
http://www.reibang.com/p/0209668d33db