iOS底层探索之Block(二)——如何解决Block循环引用问
Block你知道几种?Block的循环引用你有几种解决办法呢?
Block循环引用问题iOS底层探索之Block(一)——初识Block(你知道几种Block呢?)
在上一篇博客已经介绍了block
的类型,那么本篇博客废话不多说直接进入主题,手把手带你如何解决Block
循环引用的问题!
1. 为什么会出现循环引用分析
1.1 正常情况
在正常情况下,A
持有了B
,B
的引用计数+1
,当 A
释放的时候给 B
发送信号,B
接受到 release
信号后,引用计数-1
。
正常使用,正常释放是不会出现循环引用问题的,但是如果出现你中有我,我中有你的情况,就会出现循环引用问题了。更形象的比喻,就是鹤蚌相争故事了。
1.2 循环引用原因
互相持有都释放不了,如下图所示:
循环引用示意图这里出现循环引用问题是,
A
和 B
互相持有了,两方都释放不了,A无法调用 dealloc
就给 B
发送不了 release
消息,B
收不到 release
消息引用计数
就不会减少
,也就不能调用 dealloc
方法进行释放。
那么出现循环引用该如何解决呢?
1.3 循环引用解决思路
出现循环引用的解决办法就是,打破互相持有的局面,打破任意一方的持有都可以。
2. 解决方案
先来看看一个循环引用的例子,如下:
循环引用举例
这里出现循环引用的原因是:
self
持有了block
,block
里面又持有了self
,形成了一个闭环,互相持有,无法释放。
那么再来看看,下面👇这个代码是否出现循环引用呢?
[UIView animateWithDuration:0.25 animations:^{
self.name = @"reno";
} completion:nil];
上面👆代码是不会出现循环引用问题的,当前
UIView
持有了block
,self
并没有持有block
,所以这里不会出现循环引用问题的。
那么言归正传,出现循环引用改如何解决呢?请接着往下看!👇
2.1 __weak解决循环引用
第一种方法就是使用__weak
,这个相信大家都很熟悉了,如下:
__weak typeof(self)weakSelf = self;
self.block = ^(void){
NSLog(@"%@",weakSelf.name);
};
self.block();
这样确实没有问题,但是如果block
内部使用了延时函数
,VC
销毁的时候还是会出现问题,如下:
2.2 strong-weak-dance循环引用
__weak typeof(self)weakSelf = self;
self.block = ^(void){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",weakSelf.name);
});
};
self.block();
很多人会问:这里的问题出在哪里呢?这里是一个延时函数是异步的,当VC
销毁的时候block
也跟着释放了,延时函数内的打印name
就来不及打印,就出现如下情况:
从打印来看,
dealloc
方法调用了,VC
已经销毁了,block
也就释放了,内部的延时函数里面的打印还来不及打印,所以 name
为 null
。那么该如何解决呢?解决如下:测试结果
因为
__weak
会自动置为nil
,所以这里使用__strong
(strong-weak-dance)暂时延长self
的生命周期,使得可以正常打印。
那么这里为什么不会出现循环引用问题啊?因为这里strongSelf
是一个临时的变量,出了作用域也跟着释放了,所以不会出现循环引用,这就是强弱共舞
方法你学会了吗?
2.3 手动档方式解决循环引用
有的人喜欢手动挡的汽车,喜欢那种操控感,特别带劲,那么接下来就介绍一种手动方式解决循环引用问题。
__block ViewController *vc = self;
self.block = ^(void){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",vc.name);
vc = nil;
});
};
self.block();
这里使用一个临时的VC
变量之后,持有关系为: self --> block --> VC--> self
,VC
在block
使用完成后就被置为nil
,block
不构成对self
的持有关系了,因此这里就不构成循环引用
问题,这是不是很爽,这一波操作很溜啊!😁
这里使用
__block
是因为需要对外部变量,进行赋值操作,关注我,后续篇章会对block
进行底下的源码分析,敬请期待!
2.4 参数形式解决循环引用
上面👆手动操作方法你 get
到了吗?还有一个更哇塞的方法,如下:
typedef void(^JP_Block)(ViewController*);
@property (nonatomic, strong) JP_Block JPBlock;
- (void)parameterMethod{
self.JPBlock = ^(ViewController *vc){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",vc.name);
});
};
self.JPBlock(self);
}
这里使用参数的方式,并不会出现循环引用的问题,是不是哇塞!好简单啊!
那么解决block
循环引用问题的方法就介绍到这里了,你学会了吗?你是否还有更好的方式,欢迎评论区留言交流!
更多内容持续更新
🌹 喜欢就点个赞吧👍🌹
🌹 觉得有收获的,可以来一波,收藏+关注,评论 + 转发,以免你下次找不到我😁🌹
🌹欢迎大家留言交流,批评指正,互相学习😁,提升自我🌹