美文网首页
iOS循环引用的几种情况

iOS循环引用的几种情况

作者: woniu | 来源:发表于2018-04-18 10:51 被阅读242次

你踩过的坑里尽是前人的脚印。---------前言

在开发过程中我们会遇到一些循环引用的问题,像循环引用Block是我们最常见的循环引用问题,但是有一些循环引用是很隐蔽的,稍有不慎就会导致内存泄漏,比如GCD&NSNotification。今天我们就踩在前人的脚印上来分析循环引用这个老生常谈的话题。


循环引用的三种情况.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

相关文章

网友评论

      本文标题:iOS循环引用的几种情况

      本文链接:https://www.haomeiwen.com/subject/thzmkftx.html