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对象是在函数退出时就立即释放,这样导致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);}}
}


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/519770.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开放共赢,华为云WeLink生态联盟正式成立!

今日,华为在京发布了“更懂企业”的智能工作平台华为云WeLink,并携手合作伙伴成立华为云WeLink生态联盟。其中首批加入华为云WeLink生态联盟的伙伴主要包括(排名不分先后):金山办公、中软国际、致远互联、罗技、华为商…

指明方向与趋势!2019开发者技能报告出炉!!!

近日国外开发者平台 HankerRank 发布了 2019 年开发者技能调查报告,该报告根据对71,281开发者的调查得出。 2018 年最受欢迎的开发语言  经过调查,2018年的所有开发语言中,JavaScript是最受欢迎的语言,2017年最受欢…

webservie报文格式

<![CDATA[ 里面方内容]]>

阿里研究院入选中国企业智库系统影响力榜

2019年2月1日&#xff0c;上海社会科学院智库研究中心发布《2018年中国智库影响力评价与排名》。阿里研究院入围三项排名榜单&#xff0c;位居企业智库系统影响力榜单第2位&#xff0c;中国智库社会影响力榜单第13位&#xff0c;中国智库综合影响力排名榜单第42位。 阿里研究院…

如何给女朋友解释什么是3PC?

戳蓝字“CSDN云计算”关注我们哦&#xff01;一顿愉快的小火锅之后&#xff0c;悠哉悠哉的回家了&#xff0c;于是只能开始新一轮的家庭科普了。分布式一致性幸好在《漫话&#xff1a;如何给女朋友解释什么是2PC&#xff08;二阶段提交&#xff09;&#xff1f;》中介绍过关于2…

Tensorflow源码解析1 -- 内核架构和源码结构

1 主流深度学习框架对比 当今的软件开发基本都是分层化和模块化的&#xff0c;应用层开发会基于框架层。比如开发Linux Driver会基于Linux kernel&#xff0c;开发Android app会基于Android Framework。深度学习也不例外&#xff0c;框架层为上层模型开发提供了强大的多语言接…

如何在Kubernetes集群动态使用 NAS 持久卷

1. 介绍&#xff1a; 本文介绍的动态生成NAS存储卷的方案&#xff1a;在一个已有文件系统上&#xff0c;自动生成一个目录&#xff0c;这个目录定义为目标存储卷&#xff1b; 镜像地址&#xff1a;registry.cn-hangzhou.aliyuncs.com/acs/alicloud-nas-controller:v1.11.5.4-…

Java循环结构

顺序结构的程序语句只能被执行一次。如果您想要同样的操作执行多次,&#xff0c;就需要使用循环结构。 Java中有三种主要的循环结构&#xff1a; while 循环 do…while 循环 for 循环 一般能够确定循环次数的用for循环 不确定循环次数的用whlie 循环和 do while 循环 do while…

基于 Kubernetes 实践弹性的 CI/CD 系统

大家好,我是来自阿里云容器服务团队的华相。首先简单解释一下何为 Kubernetes 来帮助大家理解。Kuberentes 是一个生产可用的容器编排系统。Kuberentes 一方面在集群中把所有 Node 资源做一个资源池&#xff0c;然后它调度的单元是 Pod&#xff0c;当然 Pod 里面可以有多个容器…

35岁真的是一个坎吗?听完35岁码农的话,我放心了!

戳蓝字“CSDN云计算”关注我们哦&#xff01;之前看过一个有关程序员从刚入职到中年状态的一个视频&#xff0c;刚入职的程序员激情澎湃&#xff0c;一心想做自己想做的事情&#xff0c;并且想创业&#xff0c;就想拉拢身边的程序员同事一起创业&#xff0c;可是身边的同事就一…

开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题

导语 发布app后&#xff0c;开发者最头疼的问题就是如何解决交付后的用户侧问题的还原和定位&#xff0c;是业界缺乏一整套系统的解决方案的空白领域&#xff0c;闲鱼技术团队结合自己业务痛点在flutter上提出一套全新的技术思路解决这个问题。 我们透过系统底层来捕获ui事件流…

Java中数组的打印

数组不能直接打印&#xff0c;打印出来是一个地址&#xff0c;所以编写以下方法用于打印一个数组 public static void printArr(int[] arr) { for (int i : arr) { System.out.print(i" "); } } println 打印一个对象&#xff0c;默认调用这个对象的toString方法

苹果或推出Windows版Safari浏览器;微软正“取下”Chromium版Edge浏览器的Beta标签;亚马逊申请新专利……...

关注并标星星CSDN云计算 速递、最新、绝对有料。这里有企业新动、这里有业界要闻&#xff0c;打起十二分精神&#xff0c;紧跟fashion你可以的&#xff01;每周两次&#xff0c;打卡即read更快、更全了解泛云圈精彩newsgo go goSK海力士将于CES推出新款SSD&#xff1a;128层4D …

Tensorflow源码解析2 -- 前后端连接的桥梁 - Session

1 Session概述 Session是TensorFlow前后端连接的桥梁。用户利用session使得client能够与master的执行引擎建立连接&#xff0c;并通过session.run()来触发一次计算。它建立了一套上下文环境&#xff0c;封装了operation计算以及tensor求值的环境。 session创建时&#xff0c;…

为减少用户电话排队,阿里研发了智能客服调度系统

提到调度&#xff0c;大家脑海中可能想起的是调度阿里云的海量机器资源&#xff0c;而对于阿里集团客户体验事业群&#xff08;CCO&#xff09;而言&#xff0c;我们要调度的不是机器&#xff0c;而是客服资源。今天&#xff0c;我们邀请阿里高级技术专家力君&#xff0c;为大家…

@value获取不到值

private static String fastDFS;Value("${fastDFS.webUrl}") public void setfastDFS(String fastDFS) {FastdfsClientUtil.fastDFS fastDFS; }