探秘ios中block的循环引用问题

hello,小伙伴们!
ios中自从引入了block,代理就在慢慢淡出我们的视野,那么block的使用大家都了解吗?它的循环引用机制大家都了解吗?接下来我们就来聊聊这个话题.

一,介绍下简单block的写法.

//a,typedef 定义下的block写法
typedef void(^cusBlock)();
@property (copy , nonatomic)  cusBlock block;

//b,直接属性定义下的block写法
@property (copy , nonatomic)  void (^customBlock)();
//还有很多写法,大家可以参考其它牛人的写法,这里就不做介绍了.

二,执行block代码块(customBlock)

 self.customBlock = ^(){
        self.name = @"lili";
        NSLog(@"执行了block块的方法");
        
    }
    }
touchesBegan后
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    if (self.customBlock) {
        self.customBlock();
    }
    
}
输出结果:2017-02-27 19:30:46.815 text[15115:303861] 执行了block块的方法

三,block循环引用的结论

//先说结论,后面再来分析
 如果 [block内部] 访问了 [外部强引用] 对象A ,那么 [block内部] 会自动产生一个 [强引用引用] 对象A;

 如果 [block内部] 访问了 [外部弱引用] 对象A ,那么 [block内部] 会自动产生一个 [弱引用引用] 对象A;

四,分析循环引用,验证结论

a,上述代码强引用的示意图

block强引用示意图.png

这样系统会抛出一个⚠️错误

强引用警告示意图.png

执行完block代码后

block执行完成后强引用示意图.png

这个提示告诉我们,block内部出现了循环引用(有强迫症的最好解决下,免得越积越多).

b,解决办法,大家都知道的弱引用就可以完美的解决问题了.示意图如下

block弱引用示意图.png

c,特殊情况

@interface BLPerson : NSObject
@property(copy, nonatomic) NSString *name;
@property (copy , nonatomic)  void (^customBlock)();
@end
#import "BLPerson.h"

- (void)viewDidLoad{
   [super viewDidLoad];
   self.view.backgroundColor = [UIColor whiteColor];
   BLPerson *p = [[BLPerson alloc]init];
   __weak typeof(p)weakp = p;
   p.name = @"jack";
   p.customBlock = ^(){
       dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
           weakp.name = @"lili";
           NSLog(@"%@==执行了block块的方法",weakp.name);
       });
   };
   if (p.customBlock) {
       p.customBlock();
   }
}

运行结果:2017-02-28 11:17:23.519 text[4118:80908] (null)==执行了block块的方法
weakp.name =null,这是为什么了,在block里面我明明已经将 weakp.name = @"lili"赋值了啊.

经过分析这是因为,在执行dispatch_after这个block中代码的时候,由于weakp是弱引用,在这个时间差之间weakp已经被释放了,weakp为nil,意味着nil.name就没意义了.

解决办法:在customBlock里面用一个强引用在引用weakp就行了;

 p.customBlock = ^(){ 
        BLPerson *p1 = weakp;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            p1.name = @"lili";
            NSLog(@"%@==执行了block块的方法",p1.name);
        });
    };
输出结果:2017-02-28 11:26:41.679 text[4280:84156] lili==执行了block块的方法;这个时候P1.name是有值的;
ps:在block里面定义的属性是不会造成强引用的;

本章总结:
请大家牢记这两个结论,理解透了没有block会阻碍你前进的步伐.

如果 [block内部] 访问了 [外部强引用] 对象A ,那么 [block内部] 会自动产生一个 [强引用引用] 对象A;

 如果 [block内部] 访问了 [外部弱引用] 对象A ,那么 [block内部] 会自动产生一个 [弱引用引用] 对象A;

建议以后block里面如果没有特殊要求,建议都写成弱引用,避免造成自己都发现不了的bug.本文,如有误之处,请大家多多指教,我一定虚心学习,希望同大家共同进步.

推荐阅读更多精彩内容