Objective-C中的associated object释放时机问题
如果对象A持有对象B,B作为A的associated object,并且表面上B没有其他被强引用的地方,那么对象A被释放时,对象B一定会同时释放吗?大部分情况下是,但真有不是的时候。最近实现代码的时候不小心就碰到了这样的特殊情况。
需求
需要监听对象A释放(dealloc)并执行对象A的a方法。此时引入对象B,并作为对象A的associated object。A释放时触发B释放,在B的dealloc方法中执行A的a方法。对象B需要一个指向对象A的属性,并声明为unsafe_unretained(或assign),因为weak指针此时已经失效了。
示例代码
@interface MyObject1 : NSObject
@end@implementation MyObject1
- (void)foo {NSLog(@"success");
}
@end@interface MyObject2 : NSObject
@property (nonatomic, unsafe_unretained) MyObject1 *obj1;
@end@implementation MyObject2
- (void)dealloc {[self.obj1 foo];
}
+ (instancetype)create {return [[self class] new];
}
@end@implementation ViewController
+ (void)load {[self fun1];
}
+ (void)fun1 {MyObject1 *mo1 = [MyObject1 new];@synchronized (self) {MyObject2 *mo2 = [MyObject2 create];mo2.obj1 = mo1;objc_setAssociatedObject(mo1, @selector(viewDidLoad), mo2, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}
}
@end
问题
运行时出现崩溃,unsafe_unretained指针已经野了,和预期的不一样。堆栈是这样的:
观察崩溃的堆栈,发现mo2
对象是被自动释放池释放了。因为mo1
对象是在函数退出时就立即释放,这样导致mo1
比mo2
先被销毁,mo2
访问了无效指针导致了崩溃。
这个问题和@synchronized
有关系,但目前我还不知道它和arc之间有什么联系。下面给出另一个case,修改一行代码就不会崩溃了:
+ (void)fun2 {MyObject1 *mo1 = [MyObject1 new];MyObject2 *mo2 = [MyObject2 create];@synchronized (self) {mo2.obj1 = mo1;objc_setAssociatedObject(mo1, @selector(viewDidLoad), mo2, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}
}
实际上只是把mo2
的声明移动到了@synchronized
外面,堆栈变成了这样:
这时,mo2
的释放发生在调用方法的结束时。
分析
使用Hooper查看汇编代码,观察fun1
和fun2
的不同。节选出关键部分:
核心在于:fun1
中,创建mo2
后调用了retain
,fun2
中,调用的则是objc_retainAutoreleasedReturnValue
。
我们再来看看create
方法:
关键的一行在最后,调用了objc_autoreleaseReturnValue
。
关于objc_retainAutoreleasedReturnValue
和objc_autoreleaseReturnValue
,请移步 https://www.jianshu.com/p/2f05060fa377 。大意是,这两个方法成对出现时,可以优化掉[[obj autorelease] retain]
这种骚操作。
结论
在fun1
中,由于没有objc_retainAutoreleasedReturnValue
,取而代之的是retain
,导致对象被放入自动释放池。对于@synchronized
为什么会造成不同,我还没有那么深入。
因为全局自动释放池会延迟对象的释放,如果代码非常依赖对象的释放时机则会比较危险。我认为这样做是最保险的,创建一个局部自动释放池,保证局部变量在函数结束时立即释放:
+ (void)fun3 {MyObject1 *mo1 = [MyObject1 new];@autoreleasepool {@synchronized (self) {MyObject2 *mo2 = [MyObject2 create];mo2.obj1 = mo1;objc_setAssociatedObject(mo1, @selector(viewDidLoad), mo2, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}}
}
原文链接
本文为云栖社区原创内容,未经允许不得转载。
Objective-C中的associated object释放时机问题相关推荐
- Objective C 中的nil,Nil,NULL和NSNull理解
kenyo网友的原创说法是:做IOS开发的估计都对Objective-C的内存管理机制很头疼,一不小心程序就会出内存泄露,我也不例外,前几天被指针的置nil与release给搞惨了,今和大家详细解说一 ...
- 【厚积薄发系列】C++项目总结16—单例模式释放时机导致的崩溃问题分析
问题背景: 单例模式是实际项目中应用最多的设计模式,单例模式确保程序中只有唯一的一个实例.但实际应用中,引入单例后一定到清楚的知道当前单例实例的生命周期和释放时机.特别是在类似多文档的软件中,楼主以前 ...
- Java泛型中? 和 ? extends Object的异同分析
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 刘一手 来源 | 公众号「锅外的大佬」 Jav ...
- cocos2d-x游戏开发(七)对象释放时机
欢迎转载:http://blog.csdn.net/fylz1125/article/details/8521272 这篇写写自动对象的释放时机. 一个对象调用autorelease()函数后就被加入 ...
- C++提高部分_C++类模板中成员函数的创建时机---C++语言工作笔记089
然后我们再来看类模板中成员函数的创建时机是什么时候 可以看到,普通类中的成员函数一开始就可以创建了, 类模板中的成员函数在调用时才创建. 我们写个例子看看,可以看到我们写了一个Person1类,然后 ...
- redis在应用中使用连接不释放问题解决
redis在应用中使用连接不释放问题解决 参考文章: (1)redis在应用中使用连接不释放问题解决 (2)https://www.cnblogs.com/a393060727/p/5281950.h ...
- 实现LOL游戏中英雄技能的释放
今天我们要实现的是LOL中英雄技能的释放,当鼠标点击该技能或者按下代表该技能的按键时,出现技能的攻击范围圈,以及只能指示(线性指示或者范围圈指示等),在这里演示的为非指定向技能,也就是可以在范围内随意 ...
- Postgresql中的large object
1.初识postgresql large object 一位同事在对使用pg_dump备份出来的文件(使用plain格式)进行恢复时,觉得速度非常慢,让我分析一下是什么原因. 我拿到他的.bak文件, ...
- python中的object是什么意思_在Python中使用’@ patch.object’和’with patch.object’有什么区别?...
在为我的应用程序编写单元测试时,我一直在使用@ mock.patch和@ patch.object装饰器.但是现在,对于我使用装饰器的一些单元测试,我收到一个错误'TypeError:staticme ...
最新文章
- python播放音频及playsound模块解除占用的3种方法
- mongodb更新数据,查找相同的id,有重复的,就更新。
- php 单元测试 麻烦,php – 正确的单元测试
- signature=d60f979e909db97dbaa034ebe539f2fd,Experimental signatures of fermiophobic Higgs bosons
- django权限管理
- oracle参数文件initorcl位置,oracle 参数文件详解
- python提取html正文为txt,python 提取html文本的方法
- 长在华人第一学霸家族的他,到底有多牛?
- 论文笔记--基于 FCM 聚类的跨模态人物图像标注方法-2015
- input type=file美化
- 根据数据库名称glkf查看使用的用户
- 浅谈Johnson算法
- cocos creator 游戏框架
- VC++连接wifi功能(有密码)源代码
- nvm在c语言是什么意思的缩写,nvm是什么意思的缩写
- ubuntu16.04笔记本查看电脑配置(CPU,显卡,内存,硬盘)
- 给力!低代码开发平台广州流辰信息科技助您增辉创价值!
- 路由交换技术一二章总结
- 2014全国计算机等级考试大纲,2014全国计算机等级考试大纲级.doc
- php利用微信支付充值,利用thinkPHP怎么实现一个微信支付功能
热门文章
- 做为一名java高级程序员,需要了解哪些岗位?
- win7查看 本地计算机策略,win7系统本地组策略编辑器打不开怎么办
- gridview标题居中显示_Pr:制作片尾滚动字幕(旧版标题法)
- java 编译宏_java – 制作一个“宏”命令来运行程序
- 用户表如何区分普通用户和管理员_Gate.io 比特百科:什么是ETH 2.0及普通用户如何参与ETH 2.0质押挖矿...
- unicode编码 php,PHP 的 UNICODE 编码和解码
- mysql utf8mb4 造成慢_mysql使用utf8mb4经验吐血总结
- sublime运行python代码_怎么用sublime运行python
- c语言stanf,stanf
- 光电转换模块_光模块:PIN光电二极管和APD光电二极管