当前位置: 首页 > news >正文

【iOS】——Block循环引用

循环引用原因

如果在Block中使用附有_ _strong修饰符的对象类型自动变量,那么当Block从栈复制到堆时,该对象为Block所持有,这样容易引起循环引用。

HPPerson *person = [[HPPerson alloc] init];person.block = ^{NSLog(@"person.age--- %d",person.age);};

在上面代码中,person对象强持有block对象,在block语法中,block对象又强持有person对象,此时达成互相强持有,谁也无法释法谁,造成循环引用。

在这里插入图片描述

当造成block循环引用时编译器会检测出并发出警告

在这里插入图片描述

另外,如果block内没有使用self也会捕获self,引起循环引用

typedef void (^blk_t) (void);@interface HPPerson : NSObject
{blk_t _block;int _age;
}
@end
#import "HPPerson.h"@implementation HPPerson
-(id)init {self = [super init];_block = ^{NSLog(@"age = %d", _age);};return self;
}- (void)dealloc
{NSLog(@"%s", __func__);
}
@end

在这里插入图片描述

这是因为虽然没有使用self,但使用了self对象中的结构体成员,因此也会捕获self。

避免循环引用

使用weak修饰符

int main(int argc, const char * argv[]) {@autoreleasepool {HPPerson *person = [[HPPerson alloc] init];person.age = 10;__weak HPPerson *weakPerson = person;person.block = ^{NSLog(@"person.age--- %d",weakPerson.age);};NSLog(@"--------");}return 0;
}

编译完成之后是

struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;// block内部对weakPerson是弱引用HPPerson *__weak weakPerson;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, HPPerson *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};

局部变量消失时候,对于HPPerson来说,只有一个弱指针指向它,那它就销毁,然后block也销毁。在这里插入图片描述

使用__unsafe_unretained修饰符

int main(int argc, const char * argv[]) {@autoreleasepool {HPPerson *person = [[HPPerson alloc] init];person.age = 10;__unsafe_unretained HPPerson *weakPerson = person;person.block = ^{NSLog(@"person.age--- %d",weakPerson.age);};NSLog(@"--------");}return 0;
}

编译完成之后是

struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;HPPerson *__unsafe_unretained weakPerson;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, HPPerson *__unsafe_unretained _weakPerson, int flags=0) : weakPerson(_weakPerson) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};

虽然__unsafe_unretained可以解决循环引用,但是最好不要用,因为:

  • __weak:不会产生强引用,指向的对象销毁时,会自动让指针置为nil
  • __unsafe_unretained:不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变,会造成野指针

使用_ _Block修饰符

int main(int argc, const char * argv[]) {@autoreleasepool {__block HPPerson *person = [[HPPerson alloc] init];person.age = 10;person.block = ^{NSLog(@"person.age--- %d",person.age);//这一句不能少person = nil;};// 必须调用一次person.block();NSLog(@"--------");}return 0;
}

使用_ _Block修饰符解决循环引用时,需要注意的点有:

  • 在block对象中需要将_ _block变量置为nil
  • 必须调用block对象

如果不调用block对象时,会造成下面情况的循环引用:

  1. HPPerosn类对象持有Block
  2. Block持有_ _block变量
  3. _ _block变量持有HPPerson对象

在这里插入图片描述

因为block会对__block产生强引用

__block HPPerson *person = [[HPPerson alloc] init];
person.block = ^{NSLog(@"person.age--- %d",person.age);//这一句不能少person = nil;
};

person对象本身就对block是强引用

@property (copy, nonatomic) HPBlock block;

__block对person产生强引用

struct __Block_byref_person_0 {void *__isa;
__Block_byref_person_0 *__forwarding;int __flags;int __size;void (*__Block_byref_id_object_copy)(void*, void*);void (*__Block_byref_id_object_dispose)(void*);//`__block`对person产生强引用HPPerson *__strong person;
};

当执行完person = nil时候,__block解除对person的引用,进而,全都解除释放了。 但是必须调用person = nil才可以,否则,不能解除循环引用

在这里插入图片描述

强弱共舞

  • 当Block捕获self时,应该使用弱引用,这样即使Block持有self的引用,也不会阻止self被释放。
  • 由于弱引用可能变成nil,因此在Block内部使用self之前,需要检查它是否为nil
  • 为了避免在Block内部因selfnil而导致的崩溃,可以在Block的开始处使用强引用
  • 使用完成之后当Block的作用域结束之后即可释放
#import <UIKit/UIKit.h>
typedef void(^blk_t)(void);
@interface ViewController : UIViewController
@property (nonatomic, strong) blk_t block;
@property (nonatomic, copy) NSString *name;@end
#import "ViewController.h"@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.self.name = @"Hello";__weak typeof(self) weakSelf = self;self.block = ^(){dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@"%@", strongWeak.name);};self.block();
}@end

此时self持有block,block弱引用self,弱引用会自动变为nil,强持有中断,所以不会引起循环引用。但该方法可能存在中途就释放掉的问题(手动延迟,可能需要调用self.name的时候name已经被释放了)如果self被销毁,那么block则无法获取name。

因此可以改进上面的代码:

    self.name = @"Hello";__weak typeof(self) weakSelf = self;self.block = ^(){__strong __typeof(weakSelf)strongWeak = weakSelf;dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@"%@", strongWeak.name);});};self.block();

在完成block中的操作之后,才调用了dealloc方法。添加strongWeak之后,持有关系为:self -> block -> strongWeak -> weakSelf -> self。

weakSelf被强引用了就不会自动释放,因为strongWeak只是一个临时变量,它的声明周期只在block内部,block执行完毕后,strongWeak就会释放,而弱引用weakSelf也会自动释放。

参数形式解决循环引用

通过给block传参(指针拷贝)

    // 循环引用self.name = @"Hello";self.block = ^(ViewController * ctrl){dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@"%@", ctrl.name);});};self.block(self);

Block循环引用场景

    // staticSelf_定义:static ViewController *staticSelf_;- (void)blockWeak_static {__weak typeof(self) weakSelf = self;staticSelf_ = weakSelf;}

weakSelf虽然是弱引用,但是staticSelf_静态变量,并对weakSelf进行了持有,staticSelf_释放不掉,所以weakSelf也释放不掉!导致循环引用

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Java面试题基础
  • JAVA(SpringBoot)对接微信登录
  • docker compose build 怎么才能只构建其中一个服务的镜像
  • 基于微信小程序+SpringBoot+Vue的儿童预防接种预约系统(带1w+文档)
  • 夯实数字经济的“新基建”-基于大数据与区块链技术的新型基础设施
  • LangChain4j-RAG基础
  • Linux相关指令
  • Apache DolphinScheduler Worker Task执行原理解析
  • 校车购票小程序的设计
  • Perl脚本学习(一)-- 基础语法
  • 17 敏捷开发—Scrum(2)
  • 微信小程序根据动态权限展示tabbar
  • ChatGPT:@JSONField 有什么用
  • 【LeetCode】字母异位词分组
  • [Meachines] [Easy] Blocky Jar包反编译
  • JavaScript-如何实现克隆(clone)函数
  • [deviceone开发]-do_Webview的基本示例
  • 《Java编程思想》读书笔记-对象导论
  • 【干货分享】SpringCloud微服务架构分布式组件如何共享session对象
  • Angular Elements 及其运作原理
  • Cookie 在前端中的实践
  • Date型的使用
  • Docker: 容器互访的三种方式
  • echarts花样作死的坑
  • nginx 配置多 域名 + 多 https
  • PV统计优化设计
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • React-Native - 收藏集 - 掘金
  • Ruby 2.x 源代码分析:扩展 概述
  • 阿里云购买磁盘后挂载
  • 缓存与缓冲
  • 记一次删除Git记录中的大文件的过程
  • 快速构建spring-cloud+sleuth+rabbit+ zipkin+es+kibana+grafana日志跟踪平台
  • 巧用 TypeScript (一)
  • 我的面试准备过程--容器(更新中)
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • 如何在招聘中考核.NET架构师
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • # Redis 入门到精通(八)-- 服务器配置-redis.conf配置与高级数据类型
  • $(function(){})与(function($){....})(jQuery)的区别
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (4)(4.6) Triducer
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (MATLAB)第五章-矩阵运算
  • (备忘)Java Map 遍历
  • (附源码)spring boot网络空间安全实验教学示范中心网站 毕业设计 111454
  • (附源码)ssm高校升本考试管理系统 毕业设计 201631
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (剑指Offer)面试题34:丑数
  • (免费分享)基于springboot,vue疗养中心管理系统
  • *2 echo、printf、mkdir命令的应用
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .NET Core中的去虚
  • .net on S60 ---- Net60 1.1发布 支持VS2008以及新的特性
  • .NET 使用 XPath 来读写 XML 文件