【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也释放不掉!导致循环引用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/50073.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

常用Jetpack库简析

Jetpack是一组Android软件组件库&#xff0c;旨在帮助开发者更轻松地构建健壮、高效且易于维护的Android应用程序。以下是常用的Jetpack库及其主要功能和用途的理解&#xff1a; 1. ViewModel 功能与用途&#xff1a; 存储和管理与UI相关的数据&#xff0c;如Activity或Fragm…

服务韧性工程(SRE)论坛演讲实录 | 雅菲奥朗: 人工智能的未来之路引领智能运维新纪元

2024年5月24日&#xff0c;第二届服务韧性工程&#xff08;SRE&#xff09;论坛在杭州成功举办。本次会议由中关村人才协会作为指导单位&#xff0c;中国移动通信集团浙江有限公司和SRE创新联合体&#xff08;中关村人才协会SRE专委会&#xff09;联合主办,中移动信息技术有限公…

Linux 某进程 CPU 高问题,用 Shell 脚本发现处理

发现高CPU使用率进程 首先&#xff0c;我们需要编写一个Shell脚本来发现系统中CPU使用率最高的进程。以下是一个简单的脚本示例&#xff1a; #!/bin/bash# 设置 CPU 使用率的阈值,一般设置90&#xff1b;这里是demo&#xff0c;所以用30 CPU_THRESHOLD30# 获取占用 CPU 最高的…

pikauchu之Unsafe Fileupload(不安全的文件上传)

Client check&#xff08;客户检查&#xff09; 第一步先新建一个一句话木马 <?php eval($_POST[1]);?> 然后上传文件 有限制&#xff0c;只能上传那几种类型 现在看看源代码 我们将一句话木马文件的后缀改为png 然后用burp抓包&#xff0c;将png改成php 就能上传成功 …

运维上云/直播上云EasyNVS视频上云管理平台配置域名时的注意事项

EasyNVS视频上云管理平台拥有完整的视频流媒体服务能力和运维管理服务能力&#xff0c;不仅可以通过平台对EasyNVR、EasyGBS进行统一管理&#xff0c;还能解决设备现场没有固定公网IP却需要在公网直播的需求。 有用户反馈&#xff0c;在项目现场配置了EasyNVS的HTTPS证书&#…

LaTeX列表:无序列表itemize和有序列表enumerate

诸神缄默不语-个人CSDN博文目录 在LaTeX中&#xff0c;itemize和enumerate环境用于创建项目符号列表和编号列表。itemize生成无序列表&#xff08;项目符号&#xff09;&#xff0c;而enumerate生成有序列表&#xff08;编号&#xff09;。 文章目录 itemize环境enumerate环境…

Studying-代码随想录训练营day44| 1143.最长公共子序列、1035.不相交的线、53.最大子序和、392.判断子序列

第44天&#xff0c;动态规划part11&#xff0c;子序列题型part02(ง •_•)ง&#x1f4aa;&#xff0c;编程语言&#xff1a;C 目录 1143.最长公共子序列 1035.不相交的线 53.最大子序和 392.判断子序列 总结 1143.最长公共子序列 文档讲解&#xff1a;代码随想录最长公…

【微信小程序实战教程】之微信小程序原生开发详解

微信小程序原生开发详解 微信小程序的更新迭代非常频繁&#xff0c;几乎每个月都会有新版本发布&#xff0c;这就会让初学者感觉到学习的压力和难度。其实&#xff0c;我们小程序的每次版本迭代都是在现有小程序架构基础之上进行更新的&#xff0c;如果想要学好小程序开发技术&…

C++ 常量和字符定义解析

文章目录 1. 整数常量 2. 浮点常量 3. 布尔常量 4. 字符常量 5. 字符串常量 6. 定义常量 #define 预处理器 const 关键字 7. 修饰符类型 8. 类型限定符 const 实例 volatile 实例 mutable 实例 static 实例 register 实例 9. ASCII码表格 1. 整数常量 常量是固…

面试经典 114. 二叉树展开为链表

最近工作越来越难找&#xff0c;裁员越来越懂了&#xff0c;焦虑的睡不着&#xff0c;怎么办呢&#xff0c;只能刷面试题&#xff0c;卷死你们 今天这个题目没刷过&#xff0c;我思考了半天才只能用暴力&#xff0c;后来苦思冥想才想出来简单的方法&#xff0c;废话不多说&…

【机器学习】激活函数:神经网络的灵魂

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 激活函数&#xff1a;神经网络的灵魂什么是激活函数?常见激活函数类型1. Sigmo…

深入了解 JMeter 中的响应断言

JMeter 是一个强大的性能测试工具&#xff0c;能够模拟大量用户并发访问网站或应用程序&#xff0c;以便测试其性能和稳定性。在进行性能测试时&#xff0c;我们需要对响应结果进行断言&#xff0c;以确保应用程序或网站的功能和性能符合预期。 JMeter 提供了一套丰富的响应断…

VINS-FUSION 优化-IMU预积分因子(二)

VINS-FUSION 优化-IMU预积分因子(一)完成了IMU预积分及对于优化变量的全部雅克比矩阵的推导,本文结合VINS-FUSION源码,完成优化-IMU预积分因子的使用。 一、IMU预积分因子雅克比 VINS-FUSION源码中将优化变量分组如下: 1.对i时刻优化变量的雅可比

mysql中where与on区别

WHERE子句 作用范围&#xff1a;WHERE子句主要用于过滤FROM子句返回的结果集。它可以在SELECT、UPDATE、DELETE语句中使用&#xff0c;以限制哪些行被包含在最终的查询结果中&#xff0c;或者哪些行被更新或删除。应用场景&#xff1a;当需要基于某些条件过滤结果集时&#xf…

8.0.32 mysql 配置主从数据库

配置前提&#xff1a; 两台服务器都需要安装同一版本的mysql数据库 我的版本是8.0.32 数据库参数修改 主数据库my.cnf(linux)或my.ini(win)配置&#xff1a; 这里需要注意&#xff1a;server-id必须唯一主从两个库必须要不一样 log_binmysql-bin server-id24 gtid-modeON …

chapter08-面相对象编程的三大特征——封装

1、基础介绍 对电视机的操作就是典型封装 封装的好处&#xff1a;隐藏实现细节&#xff1b;可以对数据进行验证 2、封装的实现 3、入门案例 altinsert&#xff0c;getter and setter&#xff0c;自动插入

生成式人工智能的未来之路:对话系统与自主代理的交汇与展望

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

RAS--APEI 报错解析流程(2)

RAS--APEI 报错解析流程(1) 除了APEI 中除了GHES会记录错误&#xff0c;在Post过程中的错误通常是通过BERT Table汇报 1.BERT Boot Error Record Table is used to report unhandled errors that occurred in a previous boot&#xff0c;it is reported as a ‘one-time polle…

java根父类Object.clone()是深克隆还是浅克隆

在Java中&#xff0c;Object.clone() 方法实现的是浅克隆&#xff08;Shallow Clone&#xff09;。这意味着&#xff0c;当你对一个对象调用 clone() 方法时&#xff0c;它会创建一个该对象的新实例&#xff0c;但是新实例中的非静态字段是原始对象对应字段的引用拷贝&#xff…

Python学习计划——3.3作用域(局部和全局变量)

Python 中的变量作用域决定了变量的可访问性和生命周期。作用域分为局部作用域和全局作用域。 1. 局部变量 局部变量是在函数内部定义的变量&#xff0c;其作用范围仅限于函数内部。函数执行结束后&#xff0c;局部变量会被销毁。 示例&#xff1a; def my_function():x 1…