iOS彻底弄懂循环引用

@class MyObjectB;

@interface MyObjectA : NSObject

@property (nonatomic, strong) MyObjectB *objectB;

@end

@implementation MyObjectA

- (void)dealloc
{
    NSLog(@"%s",__func__);
}

@end

@class MyObjectA;

@interface MyObjectB : NSObject

@property (nonatomic, strong) MyObjectA *objectA;

@end

@implementation MyObjectB

- (void)dealloc
{
    NSLog(@"%s",__func__);
}

@end

运行以下程序:

    MyObjectA *objectA = [[MyObjectA alloc]init];
    MyObjectB *objectB = [[MyObjectB alloc]init];
    
    objectA.objectB = objectB;
    objectB.objectA = objectA;

    objectB = nil;
    objectA = nil;

并无打印信息,没有调用dealloc方法,因为形成了强引用环,如下:


这里objectA和objectB形成了环(3和4)

objectA = nil;
objectB = nil;

调用以上代码以后,如下:


objectA = nil;执行的时候,objectA的引用计数实际上是从2变成了1,因为此时还有objectB强引用它;

objectB = nil;执行的时候,objectB的引用计数实际上也是从2变成了1,因为此时还有objectA强引用它,虽然之前objectA指针无效了,但是objectA这块内存还没有被销毁;

这两行代码都没有打破环;

而此时没有任何指针指向这两块内存了,于是发生内存泄漏。


要解除循环引用,就必须打破环(3和4),有两种方式可以打破环:

方法一:

解除修改属性的引用方式如下:

@property (nonatomic, weak) MyObjectB *objectB;

方法二:

直接解除3或者4,如下:

 {
        MyObjectA *objectA = [[MyObjectA alloc]init];
        MyObjectB *objectB = [[MyObjectB alloc]init];
        
        objectA.objectB = objectB;
        objectB.objectA = objectA;
        
        objectB.objectA = nil;
        //断点一
    }
    //断点二

在断点二处有打印信息:

 -[MyObjectA dealloc]

 -[MyObjectB dealloc]

在断点一出解除了引用环,在断点二处,作用域结束,objectA和objectB都释放。


再来看一个循环引用的实例:

NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/something.dat"];
    _networkFetcher = [[EOCNetworkFetcher alloc] initWithURL:url];
    [_networkFetcher startWithCompletionHandler:^(NSData *data){
        _fetchData = data;
    }];

打破环方式:

NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/something.dat"];
    _networkFetcher = [[EOCNetworkFetcher alloc] initWithURL:url];
    [_networkFetcher startWithCompletionHandler:^(NSData *data){
        _fetchData = data;
        _networkFetcher = nil;
    }];

并保证CompletionHandler会调用。

这里把_networkFetcher = nil;提到block外面来也能打破环。

还有种方式就是不要将_networkFetcher作为controller的实例变量,而是局部变量来处理,也可以防止引用环。下面会讲到。

原因如下:



这里4,5,6形成了环,只要任意解除其中一个就行,将_networkFetcher设置为nil,实际上就是解除了4,因为这里的_networkFetcher就是controller的实例,即2就是4。

但是你不要指望在controller的dealloc方法中去将_networkFetcher置为nil,因为你只是解除了1,所以dealloc根本不会被调用。


继续看下面的例子:

NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/something.dat"];
    EOCNetworkFetcher *_networkFetcher = [[EOCNetworkFetcher alloc] initWithURL:url];
    [_networkFetcher startWithCompletionHandler:^(NSData *data){
        _fetchData = data;
    }];

有人通过这种方式来避免循环引用,但是很可能造成另外的环,如下:

NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/something.dat"];
    EOCNetworkFetcher *_networkFetcher = [[EOCNetworkFetcher alloc] initWithURL:url];
    [_networkFetcher startWithCompletionHandler:^(NSData *data){
        NSLog(@"Request URL %@ finished", _networkFetcher.url);
        _fetchData = data;
    }];

_networkFetcher和completionHandler之间形成了环,这个解决方法就是,最好是在block中将_networkFetcher作为参数传进来使用,而不是直接使用_networkFetcher。


最后我们再来看一个复杂一点的:

NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/something.dat"];
    _networkFetcher = [[EOCNetworkFetcher alloc] initWithURL:url];
    [_networkFetcher startWithCompletionHandler:^(NSData *data){
        NSLog(@"Request URL %@ finished", _networkFetcher.url);
        _fetchData = data;
    }];

这里看似有两个环,我们要怎么打破环呢?



打破环方式:

 NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/something.dat"];
    _networkFetcher = [[EOCNetworkFetcher alloc] initWithURL:url];
    [_networkFetcher startWithCompletionHandler:^(NSData *data){
        NSLog(@"Request URL %@ finished", _networkFetcher.url);
        _fetchData = data;
        _networkFetcher = nil;
    }];

并保证CompletionHandler会调用。

这里把_networkFetcher = nil;提到block外面来也能打破环。


原因如下:

这里4,5,6形成了环,5和7也形成了环,将_networkFetcher设置为nil,实际上就是解除了2,4和7,2不用解释,说解除4是因为这里的_networkFetcher就是controller的实例,是同一个指针_networkFetcher,说解除7是因为completionHandler捕获的也是_networkFetcher指针所指的对象。


猜你喜欢

转载自blog.csdn.net/junjun150013652/article/details/53908654