如果对象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对象是在函数退出时就立即释放,这样导致mo1mo2先被销毁,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查看汇编代码,观察fun1fun2的不同。节选出关键部分:

核心在于:fun1中,创建mo2后调用了retainfun2中,调用的则是objc_retainAutoreleasedReturnValue

我们再来看看create方法:

关键的一行在最后,调用了objc_autoreleaseReturnValue

关于objc_retainAutoreleasedReturnValueobjc_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释放时机问题相关推荐

  1. Objective C 中的nil,Nil,NULL和NSNull理解

    kenyo网友的原创说法是:做IOS开发的估计都对Objective-C的内存管理机制很头疼,一不小心程序就会出内存泄露,我也不例外,前几天被指针的置nil与release给搞惨了,今和大家详细解说一 ...

  2. 【厚积薄发系列】C++项目总结16—单例模式释放时机导致的崩溃问题分析

    问题背景: 单例模式是实际项目中应用最多的设计模式,单例模式确保程序中只有唯一的一个实例.但实际应用中,引入单例后一定到清楚的知道当前单例实例的生命周期和释放时机.特别是在类似多文档的软件中,楼主以前 ...

  3. Java泛型中? 和 ? extends Object的异同分析

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 刘一手 来源 | 公众号「锅外的大佬」 Jav ...

  4. cocos2d-x游戏开发(七)对象释放时机

    欢迎转载:http://blog.csdn.net/fylz1125/article/details/8521272 这篇写写自动对象的释放时机. 一个对象调用autorelease()函数后就被加入 ...

  5. C++提高部分_C++类模板中成员函数的创建时机---C++语言工作笔记089

    然后我们再来看类模板中成员函数的创建时机是什么时候 可以看到,普通类中的成员函数一开始就可以创建了, 类模板中的成员函数在调用时才创建. 我们写个例子看看,可以看到我们写了一个Person1类,然后 ...

  6. redis在应用中使用连接不释放问题解决

    redis在应用中使用连接不释放问题解决 参考文章: (1)redis在应用中使用连接不释放问题解决 (2)https://www.cnblogs.com/a393060727/p/5281950.h ...

  7. 实现LOL游戏中英雄技能的释放

    今天我们要实现的是LOL中英雄技能的释放,当鼠标点击该技能或者按下代表该技能的按键时,出现技能的攻击范围圈,以及只能指示(线性指示或者范围圈指示等),在这里演示的为非指定向技能,也就是可以在范围内随意 ...

  8. Postgresql中的large object

    1.初识postgresql large object 一位同事在对使用pg_dump备份出来的文件(使用plain格式)进行恢复时,觉得速度非常慢,让我分析一下是什么原因. 我拿到他的.bak文件, ...

  9. python中的object是什么意思_在Python中使用’@ patch.object’和’with patch.object’有什么区别?...

    在为我的应用程序编写单元测试时,我一直在使用@ mock.patch和@ patch.object装饰器.但是现在,对于我使用装饰器的一些单元测试,我收到一个错误'TypeError:staticme ...

最新文章

  1. python播放音频及playsound模块解除占用的3种方法
  2. mongodb更新数据,查找相同的id,有重复的,就更新。
  3. php 单元测试 麻烦,php – 正确的单元测试
  4. signature=d60f979e909db97dbaa034ebe539f2fd,Experimental signatures of fermiophobic Higgs bosons
  5. django权限管理
  6. oracle参数文件initorcl位置,oracle 参数文件详解
  7. python提取html正文为txt,python 提取html文本的方法
  8. 长在华人第一学霸家族的他,到底有多牛?
  9. 论文笔记--基于 FCM 聚类的跨模态人物图像标注方法-2015
  10. input type=file美化
  11. 根据数据库名称glkf查看使用的用户
  12. 浅谈Johnson算法
  13. cocos creator 游戏框架
  14. VC++连接wifi功能(有密码)源代码
  15. nvm在c语言是什么意思的缩写,nvm是什么意思的缩写
  16. ubuntu16.04笔记本查看电脑配置(CPU,显卡,内存,硬盘)
  17. 给力!低代码开发平台广州流辰信息科技助您增辉创价值!
  18. 路由交换技术一二章总结
  19. 2014全国计算机等级考试大纲,2014全国计算机等级考试大纲级.doc
  20. php利用微信支付充值,利用thinkPHP怎么实现一个微信支付功能

热门文章

  1. 做为一名java高级程序员,需要了解哪些岗位?
  2. win7查看 本地计算机策略,win7系统本地组策略编辑器打不开怎么办
  3. gridview标题居中显示_Pr:制作片尾滚动字幕(旧版标题法)
  4. java 编译宏_java – 制作一个“宏”命令来运行程序
  5. 用户表如何区分普通用户和管理员_Gate.io 比特百科:什么是ETH 2.0及普通用户如何参与ETH 2.0质押挖矿...
  6. unicode编码 php,PHP 的 UNICODE 编码和解码
  7. mysql utf8mb4 造成慢_mysql使用utf8mb4经验吐血总结
  8. sublime运行python代码_怎么用sublime运行python
  9. c语言stanf,stanf
  10. 光电转换模块_光模块:PIN光电二极管和APD光电二极管