循环引用

作者: 神采飞扬_2015 | 来源:发表于2017-01-08 23:32 被阅读369次
    • 常见的循环引用

    如下代码所示,EOCClassA实例的other属性设置成某个EOCClassB 实例,而EOCClassB实例的other属性设置成某个EOCClassA 实例,保留环会导致内存泄漏,解决方法就是将某一个引用设置为weak(即非拥有关系)。

    Paste_Image.png Paste_Image.png
    #import <Foundation/Foundation.h>
    
    @class EOCClassA;
    @class EOCClassB;
    
    @interface EOCClassA : NSObject
    @property (nonatomic, strong) EOCClassB *other;
    @end
    
    @interface EOCClassB : NSObject
    @property (nonatomic, strong) EOCClassA *other;
    @end
    
    • 代理属性设置为weak

    协议:用来指定代理双方可以做什么,必须做什么。
    代理:根据指定的协议,完成委托方需要实现的功能。
    委托:根据指定的协议,指定代理去完成什么功能。

    Paste_Image.png

    代理的本质就是代理对象内存的传递和操作,我们在委托类设置代理对象后,实际上只是用一个id类型的指针将代理对象进行了一个弱引用。委托方让代理方执行操作,实际上是在委托类中向这个id类型指针指向的对象发送消息,而这个id类型指针指向的对象,就是代理对象。

    Paste_Image.png

    实委托方的代理属性本质上就是代理对象自身,设置委托代理就是代理属性指针指向代理对象,相当于代理对象只是在委托方中调用自己的方法,如果方法没有实现就会导致崩溃。

    举例:在tableViewController中,控制器的view就是tableView,这就相当于tableViewController强引用着tableView,然后当我们设置delegate的时候,一般都是让tableViewController成为代理,这个时候代理如果也使用strong,那么tableView的delegate又强引用着tableViewController,所以导致循环引用,因此代理得用weak。

    Paste_Image.png
    • block中为避免循环引用,常见写法

    __weak typeof(self) weakSelf = self;
    [self doSomeBlockJob:^{   
       __strong typeof(weakSelf) strongSelf = weakSelf;   
       if (strongSelf) { 
         ...    
      }
    }];
    
    • 什么时候在 block 里面用 self,不需要使用 weak self

    当 block 本身不被 self 持有,而被别的对象持有,同时不产生循环引用的时候,就不需要使用 weak self 了。如: UIView动画block不会造成循环引用是因为这是类方法,不可能强引用一个类,所以不会造成循环引用。

    [UIView animateWithDuration:0.2 animations:^{
      self.alpha = 1;
    }];
    
    • 为什么 block 里面还需要写一个 strong self,不写会怎么样

    在 block 中先写一个 strong self,其实是为了避免在 block 的执行过程中,突然出现 self 被释放的尴尬情况。通常情况下,如果不这么做的话,很容易出现一些奇怪的逻辑,甚至闪退。以 AFNetworking 中 AFNetworkReachabilityManager.m
    的一段代码举例:

    __weak __typeof(self)weakSelf = self;
    AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {   
       __strong __typeof(weakSelf)strongSelf = weakSelf; 
      strongSelf.networkReachabilityStatus = status;   
      if (strongSelf.networkReachabilityStatusBlock) {  
        strongSelf.networkReachabilityStatusBlock(status);
        }
    };
    

    如果没有 strongSelf 的那行代码,那么后面的每一行代码执行时,self 都可能被释放掉了,这样很可能造成逻辑异常。特别是当我们正在执行 strongSelf.networkReachabilityStatusBlock(status);
    这个 block 闭包时,如果这个 block 执行到一半时 self 释放,那么多半情况下会 Crash。

    • 有没有这样一个需求场景,block会产生循环引用,但是业务又需要你不能使用 weak self

    场景:在网络请求API会持有回调的block,回调的block会持有self,而self也持有网络请求API的话,我们就构造了一个循环引用。虽然我们构造出了循环引用,但是因为网络请求结束时,网络请求API会主动释放对block的持有,因为整个循环链条被解开,循环应用就被打破了,所以不会存在内存泄露问题。

    - (void)clearCompletionBlock{
      //nil out to break the retain cycle 
      self.successCompletionBlock = nil; 
      self.failureCompletionBlock = nil;
    }
    

    解决思路:
    1、事前避免,我们在会产生循环引用的地方使用 weak 弱引用,以避免产生循环引用。
    2、事后补救,我们明确知道会存在循环引用,但是我们在合理的位置主动断开环中的一个引用,使得对象得以回收。

    相关文章

      网友评论

        本文标题:循环引用

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