【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 两个类。本文将详细介绍这两个类的特点和适用…...
XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
