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:
fun2:
核心在于:fun1
中,创立mo2
后调用了retain
,fun2
中,调用的则是objc_retainAutoreleasedReturnValue
。
我们再来看看create
方法:
关键的一行在最后,调用了objc_autoreleaseReturnValue
。
关于objc_retainAutoreleasedReturnValue
和objc_autoreleaseReturnValue
,请移步 https://www.songma.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); } }}
参考资料
objc_autoreleaseReturnValue和objc_retainAutoreleasedReturnValue函数对ARC的优化 https://www.songma.com/p/2f05060fa377
本文作者:三豊
阅读原文
本文为云栖社区原创内容,未经允许不得转载。
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Objective-C中的associated object释放时机问题