美文网首页
__weak与__strong是如何解决循环引用的

__weak与__strong是如何解决循环引用的

作者: 飞天猪Pony | 来源:发表于2018-06-29 15:16 被阅读8次

    当我们写一个block时,如果你的block涉及被self持有以及需要访问self的成员时,循环引用问题由此产生。解决的办法也很简单,其中利用__weak与__strong是常见的手段,类似代码如下:

    __weak typeof(self) weakSelf = self;
    self.block = ^{     
       __strong typeof(weakSelf) strongSelf = weakSelf;     
       [strongSelf print];
    };
    

    如果要把这个问题说清楚明白,不是件容易的事,看了很多文章,都没有深入全面地讲明白这个问题。为了深入剖析其中的原理,构建了一个MyTestBlock类,类的结构代码如下:

     #import <Foundation/Foundation.h> 
    @interface MyTestBlock : NSObject 
    @property (nonatomic, copy) void(^block)(); 
    @end
    
     #import "MyTestBlock.h" 
    @implementation MyTestBlock 
    - (void)dealloc{    
      NSLog(@"------>dealloc");
    }
    - (instancetype)init{    
      self = [super init];    
      if (self)    {       
         __weak typeof(self) weakSelf = self;        
        self.block = ^{ 
           __strong typeof(weakSelf) strongSelf = weakSelf;            
           [strongSelf print];        
        };    
      }    
      return self; 
    } 
    - (void)print{    
      NSLog(@"---->print");
    } 
    @end
    

    然后在main方法里测试它:

    #import <Foundation/Foundation.h>
    #import <objc/runtime.h>
    #import "MyTestBlock.h" 
    int main(int argc, const char * argv[])
    {    
      @autoreleasepool    
      {        
        MyTestBlock *testBlock = [[MyTestBlock alloc] init];        
        testBlock.block();    
      }    
      while (1) {;} //让主程序一直运行不退出    
      return 0;
    }
    

    代码中,block属性为copy,定义为copy属性是一个存放在堆的block,堆block从栈复制过程中会复制它所使用的变量的引用(非__block属性),循环引用也是因此产生。画了个简单的示意图,如下:

    对于代码中的这一步,是如何打破这个循环的呢?我们在block外的weakSelf构造了一个指向self的弱引用,如下:

    __weak typeof(self) weakSelf = self;
    

    如下图所示:

    如果你的block之行不是异步之行,在显示调用testBlock.block()时,就已经执行,此时的strongSelf变量是可以省略的,如下:

    __weak typeof(self) weakSelf = self;
    self.block = ^{       
      [weakSelf print];
    };
    

    打印结果如下:

    2017-02-03 12:13:35.881 myBlock[6131:4562526] ---->print
    2017-02-03 12:13:35.883 myBlock[6131:4562526] ------>dealloc
    

    虽然循环引用就打破了,但是新的问题又来了。那就是会有self提前于block执行之前释放的场景,testBlock实体释放了,self就指向nil了,weakSelf也会被置为nil,等block回来时,其实在向一个nil发消息。此时就用到了strongSelf,它的作用主要是应对异步执行的block,添加异步逻辑如下:

    __weak typeof(self) weakSelf = self;
    self.block =^{
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        
        [NSThread sleepForTimeInterval:5];        
        [weakSelf print];    
      });
    };
    

    此时的执行结果如下:

    2017-02-03 12:19:14.229 myBlock[6181:4566726] ------>dealloc
    

    block中的语句没有执行,添加strongSelf逻辑之后:

    __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];                   
      });
    };
    

    5s之后的打印结果如下:

    2017-02-03 13:30:23.129 myBlock[6327:4593186] ---->print
    2017-02-03 13:30:23.131 myBlock[6327:4593186] ------>dealloc
    

    针对上面的过程,补充流程图如下:

    有人看到这里,肯定会有疑惑,这里strongSelf的引用不又像前面的self一样导致了循环引用了吗?这里需要好好解释一下:

    self是一个指向实例对象的指针,它的生命周期至少是伴随着当前的实例对象的,所以一旦它和对象之间有循环引用是无法被自动打破的;strongSelf是block内部的一个局部变量,变量的作用域仅限于局部代码,而程序一旦跳出作用域,strongSelf就会被释放,这个临时产生的“循环引用”就会被自动打破,代码的执行事实上也是这样子的。

    我们可以简单地验证一下,首先我们用到一个CoreFoundation中的一个方法CFGetRetainCount,它可以在ARC下获取retainCount,修改main函数中如下:

    @autoreleasepool
    {    
      MyTestBlock *testBlock = [[MyTestBlock alloc] init];    
      NSLog(@"block执行前:%lu",CFGetRetainCount((__bridge CFTypeRef)testBlock));    
      testBlock.block();    
      NSLog(@"block执行后:%lu",CFGetRetainCount((__bridge CFTypeRef)testBlock));}
    

    执行结果如下:

    2017-02-03 13:49:34.465 myBlock[6380:4604974] block执行前:1
    2017-02-03 13:49:34.466 myBlock[6380:4604974] block执行后:2
    2017-02-03 13:49:39.469 myBlock[6380:4605000] ---->print
    2017-02-03 13:49:39.469 myBlock[6380:4605000] ------>dealloc
    

    我们发现block执行后,testBlock对象的retainCount加了1,说明block执行时,对testBlock对象临时加了强引用,但是最终对象顺利dealloc。

    原文:https://blog.csdn.net/lizitao/article/details/54845974

    相关文章

      网友评论

          本文标题:__weak与__strong是如何解决循环引用的

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