Block的循环引用探究
2018-07-18 本文已影响16人
LD_左岸
Block属性修饰用copy 在将block由栈区 拷贝到堆区后block内部会自动产生对外界的强或弱引用
- 具体是对外界产生强引用 还是弱引用 可根据以下原则判断:
如果[Block内部]使用[外界声明的强引用]访问[对象A],那么[Block内部]会产生一个[强引用]指向[对象A]
- 如下图 block内部 使用外界声明的强引用p访问对象p的name属性 那么block内部会产生一个强引用指向对象p
-
这样导致对象p对block强引用(copy)
block内部又对 对象p产生了强引用
Snip20180718_1.png
如果[Block内部]使用[外界声明的弱引用]访问[对象A],那么[Block内部]会产生一个[弱引用]指向[对象A]
Person * p = [[Person alloc]init];
p.name = @"Bulice";
__weak Person * weakP = p;
p.block = ^{
NSLog(@"%@",weakP.name);
};
block外部使用__weak
block内部使用__strong
Person * p = [[Person alloc]init];
p.name = @"Bulice";
__weak Person * weakP = p;
p.block = ^{
NSLog(@"block1---%@",weakP.name);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"block2---%@",weakP.name);
});
};
p.block();
[4356:345984] block1---Bulice
[4356:345984] -[Person dealloc]
[4356:345984] block2---(null)
- 对打印结果分析如下
由于Block1 和 Block2内部 都使用了外部的弱引用 所以Block1执行完之后 除了GCD的Block(GCD内部对延时Block有强引用)之外 剩下的对象都销毁了(局部变量)
这样就导致了延时Block里相当于 nil.name
RAC和YYKit中都有 Block外部使用弱引用 内部使用强引用的宏
他们为什么这么做
代码分析如下
- (void)viewDidLoad {
[super viewDidLoad];
Person * p = [[Person alloc]init];
p.name = @"Bulice";
__weak Person * weakP = p;
p.block = ^{
__strong Person * strongP = weakP;
NSLog(@"block1---%@",strongP.name);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"block2---%@",strongP.name);
});
};
p.block();
}
block1---Bulice
block2---Bulice
[Person dealloc]
- block1引用外部的weakP是安全的
- 在内部创建一个强指针去指向 person 对象,因为在内部声明变量,Block 是不会强引用这个对象的,这也就在避免了循环引用风险的同时,又创建出了一个强指针指向对象。
- 之后再 Block2 来引用相对于它来说是外部的变量 strongP ,
- 这时 Block2 会默认创建出来一个强引用来引用 p 对象,
- 当block1的作用域结束之后 strongP 会跟着被销毁,内存中就仅剩下了Block2 强引用着 p 对象 所以p的属性值在block2执行时 可以访问到。