美文网首页iOS开发杂货铺面试传值相关
block解决循环引用详细解释

block解决循环引用详细解释

作者: 浩杰ee | 来源:发表于2016-04-21 21:50 被阅读1855次

    一、产生原因

    网上大部分帖子都表述为:"block里面引用了self导致循环引用",会让大家理解为要显式地出现"self"就会引起循环引用。其实这种说法是很不严谨的。

    下面用代码来说明:
    *1 声明几个block和一个属性:

    @interface ViewController (){
    void(^myBlock1)(void);
    void(^myBlock2)(void);
    void(^myBlock3)(void);
     }
    @property (nonatomic,copy) NSString *person;
    @end
    

    *2 使用weakSelf 不会 引起循环引用(以下是最常用但是不提倡方法,后面会提到原因,继续看下去哦):

    __weak typeof(self) weakSelf = self;
    
    NSLog(@"init--> value:%@,address=%p,self=%p",self.person,self.person,self);
      myBlock1 = ^(void){
    //这样不会造成循环引用
    NSLog(@"execute1--> value:%@,address=%p,weakSelf=%p",weakSelf.person,weakSelf.person,weakSelf);
     };
    

    *3 直接使用self,会循环引用:Xcode会给警告

    myBlock2 = ^(void){
    //这样造成循环引用
    NSLog(@"execute2--> value:%@,address=%p,self=%p",self.person,self.person,self);
    };
    

    *4 要执行的方法抽取出来,也不会循环引用:

     myBlock3 = ^(void){
    //这样也不会造成循环引用,已经抽取出要执行的方法
    [weakSelf myBlock3Func];
     }; 
    - (void)myBlock3Func{
    NSLog(@"execute3--> value:%@,address=%p,self=%p",self.person,self.person,self);
     }
    

    *5 block不是self的属性或者变量时,在block内使用self也不会循环引用:

     //block不是self的属性时,block内部使用self也不是循环引用
     Animal *animal = [[Animal alloc] init];
     animal.animalBlock = ^(void){
     NSLog(@"animal-->    value:%@,address=%p,self=%p",self.person,self.person,self);
    };
    

    所以说并不是在block中使用self必定会循环引用,要分情况处理,如果产生了循环引用如何解决呢:

    二、解决循环引用

    *1 为了方便使用,首先定义两个宏

    #ifndef weakify
    #define weakify(o) __typeof__(o) __weak o##__weak_ = o;
         #define strongify(o) __typeof__(o##__weak_) __strong o =    o##__weak_;
    #endif
    

    *2 使用如下代码解决循环引用

    weakify(self);
    success:^(AFHTTPRequestOperation *operation, id responseObject) {
        strongify(self);
        if (!self__weak_) return ;
       //...................
       }
    

    3* 总结解释上述代码作用

    • weakify(self); 创建一个指向self的弱引用
    • strongify(self); 当加上修饰符strong时,当别处把“self”释放掉,但调用该“self”的block如果仍然没有执行结束,那么系统就会等待block执行完成后再释放,对该“self”在block中的使用起到了保护作用。当block执行结束后会自动释放掉。
    • if (!self__weak_) return ; ** 进行判断,如果在执行strongify(self)之前“self已经被释放掉了,则此时self=nil,所以直接return即可”**

    三、 代码验证

    *1 我们自定义一个类Text,在该类dealloc方法中加一行打印;

      -(void)dealloc{
     NSLog(@"dealloc %@被销毁了!!!!!!",[self class]);
    }
    

    *2 将Text作为另外一个类的属性

     @property(nonatomic,strong)Text *text;
    

    *3 测试结果

     Text *text=[[Text alloc]init];
      self.text=text;
    
    weakify(_text);
    
     //开启子线程      
          dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
    
            NSInteger count =0;
    
          strongify(_text);
         
            while( count<10) {
    
                count++;
    
                NSLog(@"---------%@---%ld",_text__weak_,(long)count);
    
                sleep(1);
    
            }
    
        });
    
     //3秒后将 text对象 销毁
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3*NSEC_PER_SEC)),dispatch_get_main_queue(), ^{
            
            self.text=nil;
            
        });
    

    4* 第一次,不使用 strongify(_text);

    屏幕快照 2016-04-21 下午9.46.30.png

    5*第二次,使用 strongify(_text);


    屏幕快照 2016-04-21 下午9.46.05.png

    可以清楚的看到,添加了strongify(_text),系统就会等待block执行完成后再释放text对象,该text对象在block中的使用起到了保护作用。当block执行结束后会自动释放掉

    相关文章

      网友评论

      • Sweet丶:block调用本质是函数调用,block会捕获并引用内部的变量,
        * 如果是从栈拷贝到堆的block需要注意循环引用的问题,在block之前对会造成循环引用的变量使用weak指针,包括循环引用和单例对象的block引用;
        * 同时为了保证block调用过程不会因为self被置位nil而导致可能的crash,需要在block里面再强引用self,保证在block调用之后才释放self。
        * 如果是在block里面还有block的情况下,最好的解决方法是在第二个block中再次使用@strongify(self)。这个也是RAC里面源码采用的方式
      • 我不是掌柜:如果是block是对象a的属性(a还有一个属性叫name),那么在block中引用a的属性时使用_name会导致循环引用,为什么呢
        我不是掌柜:@浩杰ee 我理解是属性时依赖对象而存在的,所以所谓的_name,其实是对象的_name,还是使用的self对象。不知道这样理解是否正确
        我不是掌柜:@浩杰ee _name不走getter或setter啊,而你的self.name走的是getter和setter
        浩杰ee:@kakarotto 这种情况下,在block里面用_name就等同于self.name,所以还是循环引用

      本文标题:block解决循环引用详细解释

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