你踩过的坑里尽是前人的脚印。---------前言
在开发过程中我们会遇到一些循环引用的问题,像循环引用Block是我们最常见的循环引用问题,但是有一些循环引用是很隐蔽的,稍有不慎就会导致内存泄漏,比如GCD&NSNotification。今天我们就踩在前人的脚印上来分析循环引用这个老生常谈的话题。
![](https://img.haomeiwen.com/i1249524/dedd0000a3bbd474.png)
一、Block
我们举一个最简单的例子:
[self.teacher requestData:^(NSData *data) {
self.name = @"case";
}];
这种情况是最常见的循环引用导致的内存泄漏的情况,此时,self强引用了teacher,而teacher又强引用了一个Block,该Block回调时又强引用了self,从而导致了循环引用,self无法释放。
self->teacher->block->self
解决办法
此时我们通过__weak弱引用self,在Block里面调用weakself从而打破循环。
__weak typeof(self) weakSelf = self;
[self.teacher requestData:^(NSData *data) {
//__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf.name = @"case";
}];
注意:我们在使用为什么要在Block里面添加__strong呢?在什么情况下使用__strong呢?会造成什么影响呢?下面我们就根据具体的场景来分析:
场景1:block非异步执行
在调用requestData:block时就已经执行,因为在同步情况下,self是不会在Block执行时被释放掉的了,此时的strongself变量是可以省略的。
场景2:block异步执行
//此方法在A页面,触发之后立刻进入到B页面。
self.block = ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:5];
[weakSelf print];
});
};
- (void)print
{
NSLog(@"---->print");
}
在此种场景下,Block内部的方法不会执行,但是为什么呢?原因就是self在Block执行完毕之前就释放掉了,Block实体释放了,self就指向nil,weakSelf也会被置nil,这就相当于想一个nil发送消息。__strong它的主要作用就是应对异步执行的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不是又强引用了吗,这不又成了循环引用了?实际上并非如此,__strong修饰的strongSelf是Block内部的一个局部变量,也就是说作用域仅在Block内部,一旦跳出作用域,那么我们强制产生的临时“循环引用”就会被打破,所以也就不会有循环引用的情况了。
二、GCD
GCD使用参数包含self,且Block中也包含self,那么此时就要考虑循环引用的问题了:
@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 显然这也是一个循环引用,所以也要进行弱引用处理。
tips: Facebook 开源的一个检测循环引用工具 FBRetainCycleDetector 。
四、不会造成循环引用的情况
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,所以不用考虑“循环引用”的问题。
参考资料如下
Block循环引用分析:
https://blog.csdn.net/lizitao/article/details/54845974
http://www.cocoachina.com/ios/20170122/18601.html
NSNotification循环引用分析:
https://www.jianshu.com/p/0209668d33db
网友评论