博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Objective-C中的associated object释放时机问题
阅读量:7113 次
发布时间:2019-06-28

本文共 2329 字,大约阅读时间需要 7 分钟。

如果对象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:

fun2:

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

我们再来看看create方法:

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

关于objc_retainAutoreleasedReturnValueobjc_autoreleaseReturnValue,请移步  。大意是,这两个方法成对出现时,可以优化掉[[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);        }    }}

参考资料

objc_autoreleaseReturnValue和objc_retainAutoreleasedReturnValue函数对ARC的优化 


本文作者:三豊

本文为云栖社区原创内容,未经允许不得转载。

你可能感兴趣的文章
SAP Business One 建筑施工行业方案
查看>>
毒瘤、“BM骗局”、伪区块链,陈伟星连怼EOS为哪般?
查看>>
×××群最全实操玩法
查看>>
Asp.Net MVC4入门指南(2):添加一个控制器
查看>>
AIX 5 ftp 文件传输
查看>>
5英寸小屏手机:何以统一性价比与体验?
查看>>
企业云桌面-16-配置DHCP服务器-011-DC01
查看>>
蜂巢科技发布首款创新产品“小清新”空气卫士
查看>>
理论上分析IP报文的结构各字段的意义
查看>>
哪些JavaScript IDE最好用?
查看>>
Shell中常用的引号
查看>>
企业实际应用之同步远程yum源到本地
查看>>
建议考前多熟记的知识点(1)(2)《网络工程师软考辅导——3年真题精解与闯关密卷》...
查看>>
酷客多小程序百城宣讲会-济南站圆满成功
查看>>
“风口论”,还是少鼓吹的好
查看>>
声控公司Nuance
查看>>
Photoshop制作一只可爱的卡通小鸟
查看>>
物化视图测试手册
查看>>
Windows7(32bit)用Firefly创建第一个示例
查看>>
《软件性能测试与LoadRunner实战教程》新书上市
查看>>