iOS底层探索之Block(二)——如何解决Block循环引用问题?

50 篇文章 5 订阅
5 篇文章 2 订阅

Block你知道几种?Block的循环引用你有几种解决办法呢?

Block循环引用问题

iOS底层探索之Block(一)——初识Block(你知道几种Block呢?)

在上一篇博客已经介绍了block的类型,那么本篇博客废话不多说直接进入主题,手把手带你如何解决Block循环引用的问题!

1. 为什么会出现循环引用分析

1.1 正常情况

在正常情况下,A持有了BB引用计数+1,当 A释放的时候给 B发送信号,B 接受到 release信号后,引用计数-1
正常情况示意图

正常使用,正常释放是不会出现循环引用问题的,但是如果出现你中有我,我中有你的情况,就会出现循环引用问题了。更形象的比喻,就是鹤蚌相争故事了。

1.2 循环引用原因

互相持有都释放不了,如下图所示:

循环引用示意图
这里出现循环引用问题是,AB互相持有了,两方都释放不了,A无法调用 dealloc就给 B发送不了 release消息,B 收不到 release消息引用计数不会减少,也就不能调用 dealloc方法进行释放。

那么出现循环引用该如何解决呢?

1.3 循环引用解决思路

出现循环引用的解决办法就是,打破互相持有的局面,打破任意一方的持有都可以。

2. 解决方案

先来看看一个循环引用的例子,如下:
循环引用举例

这里出现循环引用的原因是:

self持有了 blockblock 里面又持有了 self,形成了一个闭环,互相持有,无法释放。

那么再来看看,下面👇这个代码是否出现循环引用呢?

[UIView animateWithDuration:0.25 animations:^{
		self.name = @"reno";
	} completion:nil];

上面👆代码是不会出现循环引用问题的,当前 UIView持有了 blockself并没有持有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 也就释放了,内部的延时函数里面的打印还来不及打印,所以 namenull。那么该如何解决呢?解决如下:
测试结果

因为__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--> selfVCblock使用完成后就被置为nilblock不构成对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循环引用问题的方法就介绍到这里了,你学会了吗?你是否还有更好的方式,欢迎评论区留言交流!

更多内容持续更新

🌹 喜欢就点个赞吧👍🌹

🌹 觉得有收获的,可以来一波,收藏+关注,评论 + 转发,以免你下次找不到我😁🌹

🌹欢迎大家留言交流,批评指正,互相学习😁,提升自我🌹

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卡卡西Sensei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值