Block 的循环引用问题,想必使用过 Block,几乎都遇到过。今天CodeReview时,发现还存在循环引用的问题。故简单记录如下:
一、循环引用的原因
简单来说就是,【互相持有,造成内存不释放】。
如: self --持有--> block --持有--> self 形成了一个环,中间还可能穿插其他对象,反正最后形成了一个闭环,造成谁也不撒手,故内存永远不释放。
二、解决方案
也是一句话,砍掉任一一个链,破坏这个互相持有的闭环。
1、self 不持有 block
void (^testBlock)() = ^(){
self.testStr = @"Hello block";
};
testBlock(@"");
2、block 不强持有 self : weakSelf & strongSelf
__weak __typeof__(self) weakSelf = self;
__strong typeof(weakSelf) strongSelf = weakSelf;
即 Block 里对 self 为弱引用,不持有 self,即 self 的引用计数不会+1.
若仅引用一次,用weakSelf ,多次引用,则用 strongSelf 。strongSelf 是为了保证在 block 执行过程中,self 不会再变化。
至于二者区别,可以随便网上查一下,有很多人都对此进行了分享。
__weak __typeof__(self) weakSelf = self;
self.block = ^(){
__strong typeof(weakSelf) strongSelf = weakSelf;
testStr0 = @"这是 test 字符串";
NSLog(@"%@",strongSelf->testStr0);
};
self.block();
另,在 block 中对 self 的引用情况,包括使用 self 的属性,调用 self 的方法,以及使用 self 的成员变量。
__weak __typeof__(self) weakSelf = self;
self.block = ^(){
__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf->p_testStr = @"这是成员变量";
NSLog(@"%@",strongSelf->p_testStr);
};
self.block();
3、block 执行完后,置为 nil
self.testStr = @"Test Str";
self.block = ^(){
NSLog(@"%@",self.testStr);
};
self.block();
self.block = nil;
三、扩展
在总结循环引用的问题时,突发奇想这样一种情况:
若 block 中,有用了 GCD , GCD 的 block 里又用到了 self ,那该如何判断是否存在循环引用呢?
如:
void (^testBlock)() = ^(){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5. * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.testStr = @"Hello block";
NSLog(@"%@>>>gcd执行了",self.testStr);
});
};
testBlock(@"");
再如:
self.block = ^(){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5. * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.testStr = @"Hello block";
NSLog(@"%@>>>gcd执行了",self.testStr);
});
};
self.block();
其实,分析其持有关系,看有没有形成互相持有的环,若存在环,那肯定就循环引用了。
1、testBlock --持有--> GCD --持有--> self 【没有形成环,不存在循环引用】
2、self--持有--> block --持有--> GCD --持有--> self 【形成了环,故存在循环引用】
解决循环引用的方式还是如上面所述的三点即可。
附上 demo ,演示上文中提到的 block 中解决循环引用的方式。