【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对象时,会造成下面情况的循环引用:
- HPPerosn类对象持有Block
- Block持有_ _block变量
- _ _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内部因
self为nil而导致的崩溃,可以在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也释放不掉!导致循环引用
相关文章:
【iOS】——Block循环引用
循环引用原因 如果在Block中使用附有_ _strong修饰符的对象类型自动变量,那么当Block从栈复制到堆时,该对象为Block所持有,这样容易引起循环引用。 HPPerson *person [[HPPerson alloc] init];person.block ^{NSLog("person.age--- …...
shell脚本自动化安装启动各种服务
1、自动化配置dns服务器 A主机:vim dns.sh #!/bin/bash# 自动化部署dns# 1、下载bind# 2、修改配置文件# vim /etc/named.conf # listen-on port 53 { 127.0.0.1;any; }; 修改(定位替换)# allow-query { localhost;any; }; 修改&am…...
Python - 开源库 ReportLab 库合并 CVS 和图像生成 PDF 文档
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/140281680 免责声明:本文来源于个人知识与公开资料,仅用于学术交流,欢迎讨论,不支持转载。 Report…...
Java编写SIP协议
1、编写Server代码 package com.genersoft.iot.vmp.sip; import javax.sip.*; import javax.sip.message.*; import javax.sip.header.*; import java.util.*;public class SimpleSipServer implements SipListener {private SipFactory sipFactory;private SipStack sipStack…...
大型语言模型LLM的核心概念
本文主要介绍了目前主流的,几个大型语言模型LLM的整个训练过程 通常分为下面的几个阶段 1. 预训练 采用互联网上的大量数据进行训练,这一阶段大模型LLM的主体已定,找出共性并且压缩成一个模型。模型的参数量不是越大越好,遵循合理…...
软件测试---网络基础、HTTP
一、网络基础 (1)Web和网络知识 网络基础TCP/IP 使用HTTP协议访问Web WWW万维网的诞生 WWW万维网的构成 (2)IP协议 (3)可靠传输的TCP和三次握手策略 (4)域名解析服务DNS ࿰…...
韩顺平0基础学java——第39天
p820-841 jdbc和连接池 1.JDBC为访问不同的数据库提供了统一的接口,为使用者屏蔽了细节问题。 2.Java程序员使用JDBC,可以连接任何提供了JDBC驱动程序的数据库系统,从而完成对数据库的各种操作。 3.jdbc原理图 JDBC带来的好处 2.JDBC带来的…...
Linux文件恢复
很麻烦 一般还是小心最好 特别恢复的时候 可能不能选择某个文件夹去扫描恢复 所以 删除的时候 用rm -i代替rm 一定小心 以及 探索下linux的垃圾箱机制 注意 一定要恢复到不同文件夹 省的出问题 法1 系统自带工具 debugfs 但是好像不能重启? testdisk 1、安装 …...
大数据的数据质量有效提升的研究
大数据的数据质量有效提升是一个涉及多个环节和维度的复杂过程。以下是从数据采集、处理、管理到应用等方面,对大数据数据质量有效提升的研究概述: 一、数据采集阶段 明确采集需求:在数据采集前,需明确数据需求,包括…...
Flink-CDC解析(第47天)
前言 本文主要概述了Flink-CDC. 1. CDC 概述 1.1 什么是CDC? CDC是(Change Data Capture 变更数据获取)的简称 ,在广义的概念上,只要是能捕获数据变更的技术,都可以称之为 CDC。 核心思想是,…...
二阶段测试
二阶段测试 1、部署框架前准备工作 服务器类型部署组件ip地址DR1调度服务器 主(ha01)KeepalivedLVS-DR192.168.168.21DR2调度服务器 备 (ha02)KeepalivedLVS-DR192.168.168.22web1节点服务器 (slave01)NginxTomcatMySQL 备MHA managerMHA node192.168.1…...
CSP-J模拟赛day1——解析+答案
题目传送门 yjq的吉祥数 题解 送分题,暴力枚举即可 Code #include<bits/stdc.h> using namespace std;int l,r; int num1,tmp0,q[10000],a[10000]; int k (int x){for (int j1;j<tmp;j){if (xq[j])return 0;}return 1; } int main(){while (num<100…...
【PostgreSQL案例】我要查的表没有在执行计划中
问题:查的表没有在执行计划中 sql: SELECT* FROM(SELECTA.column1 as "column1",--中间省略很多A字段A.column99 as "column99"fromtable_a Aleft join (SELECTlzl_idfromtable_a AAinner join table_b BB ON AA.lzl_key BB.lzl_…...
《程序猿入职必会(5) · CURD 页面细节规范 》
📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…...
操作系统面试知识点总结5
#来自ウルトラマンメビウス(梦比优斯) 1 IO管理概述 1.1 I/O 设备 I/O 设备的类型分类。 1.1.1 按使用特性 人机交互类外部设备,例如打印机、显示器等。存储设备,例如磁盘、光盘等。网络通信设备,例如网络接口等。 1…...
BigInteger和BigDecimal类
一、应用场景 1. BigInteger 类 目前,我们学过最大的是long类型,但是,在实际开发时候,很有可能遇见超出long类型范围的数,我们就需要用BigInteger类; ① add 加 ② subtract 减 ③ multiply 乘…...
2024最新Uniapp的H5网页版添加谷歌授权验证
现在教程不少,但是自从谷歌升级验证之后,以前的老教程就失效了,现在写一个新教程以备不时之需。 由于众所周知的特殊原因,开发的时候一定注意网络环境,如果没有梯子是无法进行开发的哦~ clientID的申请方式我就不再进…...
学习java第一百四十四天
Spring通知有哪些类型? 在AOP术语中,切面的工作被称为通知。通知实际上是程序运行时要通过Spring AOP框架来触发的代码段。 Spring切面可以应用5种类型的通知: 前置通知(Before):在目标方法被调用之前调用通…...
Meta 发布 Llama3.1,一站教你如何推理、微调、部署大模型
最近这一两周看到不少互联网公司都已经开始秋招提前批了。不同以往的是,当前职场环境已不再是那个双向奔赴时代了。求职者在变多,HC 在变少,岗位要求还更高了。 最近,我们又陆续整理了很多大厂的面试题,帮助一些球友解…...
XSSFWorkbook 和 SXSSFWorkbook 的区别
在现代办公环境中,处理 Excel 文件是一个常见的任务。Apache POI 是一个流行的 Java 库,能够读写 Microsoft Office 文档。对于处理 Excel 文件,Apache POI 提供了 XSSFWorkbook 和 SXSSFWorkbook 两个类。本文将详细介绍这两个类的特点和适用…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
Unity UGUI Button事件流程
场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...
goreplay
1.github地址 https://github.com/buger/goreplay 2.简单介绍 GoReplay 是一个开源的网络监控工具,可以记录用户的实时流量并将其用于镜像、负载测试、监控和详细分析。 3.出现背景 随着应用程序的增长,测试它所需的工作量也会呈指数级增长。GoRepl…...
C++ 类基础:封装、继承、多态与多线程模板实现
前言 C 是一门强大的面向对象编程语言,而类(Class)作为其核心特性之一,是理解和使用 C 的关键。本文将深入探讨 C 类的基本特性,包括封装、继承和多态,同时讨论类中的权限控制,并展示如何使用类…...
React 进阶特性
1. ref ref 是 React 提供的一种机制,用于访问和操作 DOM 元素或 React 组件的实例。它可以用于获取某个 DOM 元素的引用,从而执行一些需要直接操作 DOM 的任务,例如手动设置焦点、选择文本或触发动画。 1.1. 使用 ref 的步骤 1. 创建一个 ref:使用 React.createRef 或 …...
