如何避免block循环引用

从0到1带大家搭建spring cloud alibaba 微服务大型应用框架(十一)spring-boot-admin 监控篇(2)springcloud 集成spring boot admin 从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(十一)spring-boot-admin 监控篇(2)springcloud 集成spring boot admin spring-boot-admin 服务端创建与集成入nacos spring-boot-admin 查看日志 阅读详情

本文章转载至http://honglu.me/2015/01/06/weak%E4%B8%8Eblock%E5%8C%BA%E5%88%AB/

__weak与__block区别

公司最近在招 iOS,我面试了几个人,问到 block 避免循环引用的问题时,发现好多人都说通过添加 __block 修饰词来避免。再加上我对__block和__weak也没有区分的太明确,搞得我都有点儿怀疑我自己以前是不是用错了。正好借这个机会来一探究竟~

准备工作

首先我定义了一个类 MyObject 继承 NSObject,并添加了一个属性 text,重写了description方法,返回 text 的值。这个主要是因为编译器本身对 NSString 是有优化的,创建的 string 对象有可能是静态存储区永不释放的,为了避免使用 NSString 引起一些问题,还是创建一个 NSObject 对象比较合适。

另外我自定义了一个 TLog 方法输出对象相关值,定义如下:

#define TLog(prefix,Obj) {NSLog(@"变量内存地址:%p, 变量值:%p, 指向对象值:%@, --> %@",&Obj,Obj,Obj,prefix);}

__weak

我们测试下面一段代码

MyObject *obj = [[MyObject alloc]init];
obj.text = @"my-object";
TLog(@"obj", obj);

__weak MyObject *weakObj = obj;
TLog(@"weakObj", weakObj);

void(^testBlock)() = ^(){
    TLog(@"weakObj - block", weakObj);
};
testBlock();
obj = nil;
testBlock();

输出:

变量内存地址:0x7fff58c8a9f0, 变量值:0x7f8e0307f1d0, 指向对象值:my-object, --> obj
变量内存地址:0x7fff58c8a9e8, 变量值:0x7f8e0307f1d0, 指向对象值:my-object, --> weakObj
变量内存地址:0x7f8e030804c0, 变量值:0x7f8e0307f1d0, 指向对象值:my-object, --> weakObj - block
变量内存地址:0x7f8e030804c0, 变量值:0x0, 指向对象值:(null), --> weakObj - block

从上面的结果可以看到

  • block 内的 weakObj 和外部的 weakObj 并不是同一个变量
  • block 捕获了 weakObj 同时也是对 obj 进行了弱引用,当我在 block 外把 obj 释放了之后,block 内也读不到这个变量了
  • 当 obj 赋值 nil 时,block 内部的 weakObj 也为 nil 了,也就是说 obj 实际上是被释放了,可见 __weak 是可以避免循环引用问题的

接下来我们再看第二段代码

MyObject *obj = [[MyObject alloc]init];
obj.text = @"my-object";
TLog(@"obj", obj);
    
__weak MyObject *weakObj = obj;
TLog(@"weakObj-0", weakObj);
    
void(^testBlock)() = ^(){
   __strong MyObject *strongObj = weakObj;
   TLog(@"weakObj - block", weakObj);
   TLog(@"strongObj - block", strongObj);
};

TLog(@"weakObj-1", weakObj);
testBlock();
TLog(@"weakObj-2", weakObj);
obj = nil;
testBlock();
TLog(@"weakObj-3", weakObj);

输出

变量内存地址:0x7fff5d7b2d18, 变量值:0x7fcf78c11e80, 指向对象值:my-object, --> obj
变量内存地址:0x7fff5d7b2d10, 变量值:0x7fcf78c11e80, 指向对象值:my-object, --> weakObj-0
变量内存地址:0x7fff5d7b2d10, 变量值:0x7fcf78c11e80, 指向对象值:my-object, --> weakObj-1
变量内存地址:0x7fcf78f0f520, 变量值:0x7fcf78c11e80, 指向对象值:my-object, --> weakObj - block
变量内存地址:0x7fff5d7b2bb8, 变量值:0x7fcf78c11e80, 指向对象值:my-object, --> strongObj - block
变量内存地址:0x7fff5d7b2d10, 变量值:0x7fcf78c11e80, 指向对象值:my-object, --> weakObj-2
变量内存地址:0x7fcf78f0f520, 变量值:0x0, 指向对象值:(null), --> weakObj - block
变量内存地址:0x7fff5d7b2bb8, 变量值:0x0, 指向对象值:(null), --> strongObj - block
变量内存地址:0x7fff5d7b2d10, 变量值:0x0, 指向对象值:(null), --> weakObj-3

如果你看过 AFNetworking 的源码,会发现 AFN 中作者会把变量在 block 外面先用 __weak 声明,在 block 内把前面 weak 声明的变量赋值给 __strong 修饰的变量这种写法。

从上面例子我们看到即使在 block 内部用 strong 强引用了外面的 weakObj ,但是一旦 obj 释放了之后,内部的 strongObj 同样会变成 nil,那么这种写法又有什么意义呢?

下面再看一段代码:

MyObject *obj = [[MyObject alloc]init];
obj.text = @"my-object";
TLog(@"obj", obj);
    
__weak MyObject *weakObj = obj;
TLog(@"weakObj-0", weakObj);
    
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
   __strong MyObject *strongObj = weakObj;
   TLog(@"weakObj - block", weakObj);
   TLog(@"strongObj - block", strongObj);
   
   sleep(3);
   
   TLog(@"weakObj - block", weakObj);
   TLog(@"strongObj - block", strongObj);
});
NSLog(@"------ sleep 1s");
sleep(1);
obj = nil;
TLog(@"weakObj-1", weakObj);
NSLog(@"------ sleep 5s");
sleep(5);
TLog(@"weakObj-2", weakObj);

执行结果:

变量内存地址:0x7fff58e2ad18, 变量值:0x7fa2b1e804e0, 指向对象值:my-object, --> obj
变量内存地址:0x7fff58e2ad10, 变量值:0x7fa2b1e804e0, 指向对象值:my-object, --> weakObj-0
变量内存地址:0x7fa2b1e80710, 变量值:0x7fa2b1e804e0, 指向对象值:my-object, --> weakObj - block
变量内存地址:0x700000093de8, 变量值:0x7fa2b1e804e0, 指向对象值:my-object, --> strongObj - block
------ sleep 1s
变量内存地址:0x7fff58e2ad10, 变量值:0x7fa2b1e804e0, 指向对象值:my-object, --> weakObj-1
------ sleep 5s
变量内存地址:0x7fa2b1e80710, 变量值:0x7fa2b1e804e0, 指向对象值:my-object, --> weakObj - block
变量内存地址:0x700000093de8, 变量值:0x7fa2b1e804e0, 指向对象值:my-object, --> strongObj - block
变量内存地址:0x7fff58e2ad10, 变量值:0x0, 指向对象值:(null), --> weakObj-2

代码中使用 sleep 来保证代码执行的先后顺序。从结果中我们可以看到,只要 block 部分执行了,即使我们中途释放了 obj,block 内部依然会继续强引用它。对比上面代码,也就是说 block 内部的 __strong 会在执行期间进行强引用操作,保证在 block 内部 strongObj 始终是可用的。这种写法非常巧妙,既避免了循环引用的问题,又可以在 block 内部持有该变量。

综合两部分代码,我们平时在使用时,常常先判断 strongObj 是否为空,然后再执行后续代码,如下方式:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
   __strong MyObject *strongObj = weakObj;
   if (strongObj) {
       // do something ...
   }
});

这种方式先判断 Obj 是否被释放,如果未释放在执行我们的代码的时候保证其可用性。

__block

先上代码

MyObject *obj = [[MyObject alloc]init];
obj.text = @"my-object-1";
TLog(@"obj",obj);

__block MyObject *blockObj = obj;
obj = nil;
TLog(@"blockObj -1",blockObj);

void(^testBlock)() = ^(){
    TLog(@"blockObj - block",blockObj);
    MyObject *obj2 = [[MyObject alloc]init];
    obj2.text = @"my-object-2";
    TLog(@"obj2",obj2);
    blockObj = obj2;
    TLog(@"blockObj - block",blockObj);
};
NSLog(@"%@",testBlock);
TLog(@"blockObj -2",blockObj);
testBlock();
TLog(@"blockObj -3",blockObj);

结果

变量内存地址:0x7fff5021a9f0, 变量值:0x7ff6b48d8cd0, 指向对象值:my-object-1, --> obj
变量内存地址:0x7fff5021a9e8, 变量值:0x7ff6b48d8cd0, 指向对象值:my-object-1, --> blockObj -1
<__NSMallocBlock__: 0x7ff6b48d8c20>
变量内存地址:0x7ff6b48da518, 变量值:0x7ff6b48d8cd0, 指向对象值:my-object-1, --> blockObj -2
变量内存地址:0x7ff6b48da518, 变量值:0x7ff6b48d8cd0, 指向对象值:my-object-1, --> blockObj - block
变量内存地址:0x7fff5021a7f8, 变量值:0x7ff6b48d9960, 指向对象值:my-object-2, --> obj2
变量内存地址:0x7ff6b48da518, 变量值:0x7ff6b48d9960, 指向对象值:my-object-2, --> blockObj - block
变量内存地址:0x7ff6b48da518, 变量值:0x7ff6b48d9960, 指向对象值:my-object-2, --> blockObj -3

可以看到在 block 声明前后 blockObj 的内存地址是有所变化的,这涉及到 block 对外部变量的内存管理问题,大家可以看扩展阅读中的几篇文章,对此有较深入的分析。

下面来看看 __block 能不能避免循环引用的问题

MyObject *obj = [[MyObject alloc]init];
obj.text = @"11111111111111";
TLog(@"obj",obj);

__block MyObject *blockObj = obj;
obj = nil;
void(^testBlock)() = ^(){
    TLog(@"blockObj - block",blockObj);
};
obj = nil;
testBlock();
TLog(@"blockObj",blockObj);

输出

变量内存地址:0x7fff57eef9f0, 变量值:0x7ff86a55a160, 指向对象值:11111111111111, --> obj
变量内存地址:0x7ff86c918a88, 变量值:0x7ff86a55a160, 指向对象值:11111111111111, --> blockObj - block
变量内存地址:0x7ff86c918a88, 变量值:0x7ff86a55a160, 指向对象值:11111111111111, --> blockObj

当外部 obj 指向 nil 的时候,obj 理应被释放,但实际上 blockObj 依然强引用着 obj,obj 其实并没有被真正释放。因此使用 __block并不能避免循环引用的问题。

但是我们可以通过手动释放 blockObj 的方式来释放 obj,这就需要我们在 block 内部将要退出的时候手动释放掉 blockObj ,如下这种形式

MyObject *obj = [[MyObject alloc]init];
obj.text = @"11111111111111";
TLog(@"obj",obj);

__block MyObject *blockObj = obj;
obj = nil;
void(^testBlock)() = ^(){
    TLog(@"blockObj - block",blockObj);
    blockObj = nil;
};
obj = nil;
testBlock();
TLog(@"blockObj",blockObj);

这种形式既能保证在 block 内部能够访问到 obj,又可以避免循环引用的问题,但是这种方法也不是完美的,其存在下面几个问题

  • 必须记住在 block 底部释放掉 block 变量,这其实跟 MRC 的形式有些类似了,不太适合 ARC
  • 当在 block 外部修改了 blockObj 时,block 内部的值也会改变,反之在 block 内部修改 blockObj 在外部再使用时值也会改变。这就需要在写代码时注意这个特性可能会带来的一些隐患
  • __block 其实提升了变量的作用域,在 block 内外访问的都是同一个 blockObj 可能会造成一些隐患

总结

__weak 本身是可以避免循环引用的问题的,但是其会导致外部对象释放了之后,block 内部也访问不到这个对象的问题,我们可以通过在 block 内部声明一个 __strong 的变量来指向 weakObj,使外部对象既能在 block 内部保持住,又能避免循环引用的问题。

__block 本身无法避免循环引用的问题,但是我们可以通过在 block 内部手动把 blockObj 赋值为 nil 的方式来避免循环引用的问题。另外一点就是 __block 修饰的变量在 block 内外都是唯一的,要注意这个特性可能带来的隐患。

【漏洞复现】Apache Tomcat远程代码执行漏洞(CVE-2024-56337) CVE-2024-56337是一个存在于Apache Tomcat中的TOCTOU(Time-of-Check Time-of-Use)竞争条件漏洞。此漏洞源于对先前漏洞(CVE-2024-50379)的不完整修复。若默认servlet是可写的(参数readonly设置值为false,该设置为非默认设置)表示不区分大小写文件系统,在负载下并发读取和上传同一文件可以绕过Tomcat的大小写检查。攻击者可以通过构造恶意请求上传含恶意JSP代码的文件,使其触发解析并执行这些文件,从而导致远程代码执行。 阅读详情

相关推荐

微信小程序返回按钮的隐藏玩法:用onShow实现数据自动刷新(附完整代码)

本文深入探讨了微信小程序中用户点击返回按钮后,如何利用onShow生命周期函数实现页面数据的自动刷新。文章对比了onLoad与onShow的差异,提供了从基础的本地存储标记到进阶的EventChannel事件通信,再到状态管理库的多种解决方案,并附有完整的代码示例,帮助开发者构建流畅的用户体验。

agile9scrum的博客 925

__weak与__block区别,深层理解两者区别

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33998125的博客 164

用Linkboy玩转Arduino:3小时搞定智能倒车雷达(附仿真文件下载)

本文详细介绍了如何利用Linkboy图形化编程工具,在3小时内快速搭建一个基于Arduino的智能倒车雷达项目。通过仿真环境,初学者无需硬件即可学习超声波测距原理、报警逻辑设计,并完成从虚拟仿真到实体硬件的完整流程。文章提供了清晰的步骤、调试技巧及功能扩展思路,并附有仿真文件,是入门Arduino和智能硬件制作的绝佳实践指南。

weixin_29159441的博客 403

使用block时如何避免循环引用

RT - (void)dealloc { NSLog(@"no cycle retain"); } - (id)init { self = [super init]; if (self) { #if TestCycleRetainCase1 //会循环引用 self.myblock = ^{

xietao3 1302

从“破解”到应用:三菱数控系统M80协议逆向分析与工业数据采集网关搭建指南

本文深入解析三菱数控系统M80协议的逆向分析方法与工业数据采集网关搭建技术。通过TCP协议实现跨平台通信,详细介绍了M80协议的数据包结构、多语言实现方案(C#、Java、C/C++)以及Linux数据采集网关的架构设计与容器化部署。为工业自动化领域的开发者提供从协议分析到企业级数据采集落地的完整解决方案。

weixin_29228781的博客 1212

Buck的振铃实验与分析

上上期我们提到了buck电路的开关的振铃波形,本质原因是LC的阻尼振荡。文章偏理论,那BUCK到底是怎么产生尖峰振荡呢? 要想把这个问题搞清楚,也很是不容易,所以文章有点长,请直接点赞转发加收藏。 问题 本期主要分析以下这两个问题: 1、死区时间是什么?这里有个小台阶是什么情况? 2、上下尖峰振荡是如何产生的?跟哪些因素有关? 理想的BUCK的SW波形 我们由浅入深,一步一步来,先看理想的开关SW波形—没有尖峰电压的波形。 为了能更好的看buck电路各个点的电压电流情况.

weixin_42005993的博客 3万+

微信小程序 - block 与 view 的区别

在微信小程序开发中 <view>:是一个标签,它会在页面显示出来。 <block>:是一个包装块,也就是代码块,里面可以包装一个专门的代码模块或者模板,但是它不会被显示到页面上,只会将包装的内容显示到页面上。 用 <view> 进行包装 用 <block> 进行包装 这就是 <view> 与 <block> 的主要区别,所以在使用的使用就需要更具自己的需求去进行使用,是否需要外层的包装视图出现,还是只是单纯的用于包..

卡尔特斯 8538

【0000】PostgreSQL V17.4 内核源码剖析专栏(2017 ~ 2025.11.28 更新)

本专栏将会持续更新,直到将PostgreSQL V13.2源码涉及的所有原理、技术剖析完成为止。每一篇文章都是经过多次的源码阅读 + 调试,得以验证之后才梳理成文章形式。最终该专栏的文章数量应该在一万篇或以下,这也是文章序号从【0000】开始的缘故。通过本专栏的学习,能够让你掌握PostgreSQL数据库中的每个技术背后,底层的实现原理。

Postgres 数据库内核开发工程师 1万+

微信小程序中,block标签与view标签的区别及实例应用

介绍 在微信小程序开发当中,除了最常使用的 <view> 标签以外, 还有 <block> 标签; 这两个标签还是有很明显的区别的;具体如下: 定义 <view> 是一个组件,它可以在页面上做渲染 <block> 不是组件,它只是一个包装元素;它不会在页面中做任何的渲染,它只接收控制属性 代码展示: <view> wxml: <view class="list-box"> <view class="list"

Shivy_的博客 7028

Veritas NetBackup8.1.1客户端安装

1.在客户机打开安装包运行Browser 2.选择NetBackup Client Software Installation 3.下一步 4.接受,下一步 5.我选择自定义安装,下一步 6.我选择安装到D盘 7.默认下一步 8.使用域管理账户 9.输入服务器安装了服务端的那一台名称 10.输入服务端生成的令牌,选择“I recognize the fingerprint for this host.Proceed with the certificate deploymen”下一步

万物皆可学 2万+

VXWorks6.9 + Workbench3.3 开发环境部署

VxWorks6.9 + Workbench3.3的安装部署,以及简单示例项目的创建、编译和运行

Mr.Sugarcane 5458

微信小程序中block和View组件的使用区别

总结来说,block组件主要用于逻辑分组和控制,不会在页面中生成实际节点;而View组件是用于显示内容和布局的,可以设置样式属性,生成实际的节点。

华山论码 2900

银行监管报送系统介绍(十五):金融审计平台

金审平台主要包括基础、财务、结算、信贷和中间业务五大板块,主要服务于审计署对中国银行的各项审计任务,同时也可为行内审计、稽核检查、数据分析、总分行提供基础数据服务,为实现基础数据共享提供了有力的数据保障。2019年1月某全国性股份制商业银行收到审计署2019-23号文,文中指出为进一步提高审计数据质量,审计署审计组派员前往9家重点商业银行,就完善标准化数据的相关情况与各银行有关部门进行讨论协商。关于数据报送频率及报送内容:数据从2019年4月开始,按月报送,即于每月25日之前报送上月数据;

银行信息系统应用架构详解 2053

如何避免在Block里用self造成循环引用

一般来说我们总会在设置Block之后,在合适的时间回调Block,而不希望回调Block的时候Block已经被释放了,所以我们需要对Block进行copy,copy到堆中,以便后用。 当一个Block被Copy的时候,如果你在Block里进行了一些调用,那么将会有一个强引用指向这些调用方法的调用者,有两个规则

Bannings的专栏 6万+

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

避免Block的循环引用

  避免Block的循环引用 什么是循环引用,什么时候发生循环引用 1 循环引用就是当self 拥有一个block的时候,在block 又调用self的方法。形成你中有我,我中有你,谁都无法将谁释放的困局。 self.myBlock = ^{ [self doSomething]; }; +-----------+ +-------...

weixin_33801856的博客 186

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

正确使用Block避免循环引用(一)

正确使用Block避免循环引用

会写程序的小帅比 2085

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

在block中使用self怎么避免循环引用

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33979363的博客 153

iOS进阶之底层原理-block本质、block的签名、__block、如何避免循环引用

面试的时候,经常会问到block,学完本篇文章,搞通底层block的实现,那么都不是问题了。 block的源码是在libclosure中。 我们带着问题来解析源码: blcok的本质是什么 block的方法签名 __block的作用 如何避免循环引用 block种类 block总共有6种,常用的是3种_NSConcreteStackBlock、_NSConcreteAutoBlock、_NSConcreteGlobalBlock,其他三种是系统调用。 void * _NSConcreteS

hengsf123456的博客 545

IOS 如何避免Block的循环引用

引用大神的文章 避免Block的循环引用 什么是循环引用,什么时候发生循环引用 1 循环引用就是当self 拥有一个block的时候,在block 又调用self的方法。形成你中有我,我中有你,谁都无法将谁释放的困局。 self.myBlock = ^{ [self doSomething]; }; +-----------+

baidu_31326173的博客 515

iOS中block的详解weakSelf、strongSelf,以及block循环引用的避免。

1我们知道,在使用 block 的时候,为了避免产生循环引用,通常需要使用 weakSelf 与 strongSelf,写下面这样的代码:__weak typeof(self) weakSelf = self; [self doSomeBlockJob:^{ __strong typeof(weakSelf) strongSelf = weakSelf; if (strongSel...

qq_15309157的博客 742

iOS避免Block引起循环引用的标准处理方式

首先: 在工程的Prefix.pch的文件中直接(不推荐)或在其导入的头文件中间接写入以下宏定义:   //----------------------强弱引用---------------------- #ifndef weakify     #if DEBUG         #if __has_feature(objc_arc)         #define weakif...

W2i0l1l5y的博客 635

block里的self,避免循环引用

block是可以捕捉上下文的特殊代码块。 block可以访问定义在block外的变量,当在block中使用时,它就会为其在作用域内的每个标量变量创建一个副本。 如果通过self拥有一个block,然后又在block中改变了实例变量,就会出错。 例如: 1 self.block = ^(NSString *aString) 2 { 3 self.aLabel.text = a

mnbvcxz111123的博客 403

私有成员变量在block如何避免循环引用

最近,接手外包公司的项目,项目中用到大量的block,我们都知道block相对于代理是比较方便的,但是需要注意的就是如何避免循环应用导致的内存泄漏的问题,毫无疑问,外包的项目代码质量果然不太理想,经过测试存在大量的循环引用导致的内存泄漏—–控制器释放不了。 一般的用@property修饰的我们都比较熟悉怎么避免循环引用只要加上 __weak typeof (self)weak = se

lybeen2007的博客 2630
上一篇: ios 3dTouch----详细
下一篇: iOS事件响应链中Hit-Test View的应用
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值