【技术求助】辅助类对象初始化后立即销毁,存属性可用却嫌冗余
这是个非常典型的ARC内存管理问题,我之前做截图监听工具的时候也碰到过一模一样的情况——临时创建的实例因为没有强引用持有,方法执行完就被ARC自动释放了,导致刚订阅完事件就触发了dealloc。下面给你几个不用显式添加属性的可行方案:
原因拆解
首先得明确为什么会出现这个问题:在ARC环境下,如果你只是在某个方法内部临时创建CustomOnScreenshot实例,没有任何强引用(比如属性、全局变量)指向它,那当方法执行完毕,这个实例的引用计数会直接降到0,系统就会立刻销毁它,自然没法正常监听后续的截图事件。而把实例存为属性,本质就是给它加了一个强引用,让它能存活到你需要的生命周期。
可行解决方案
1. 用关联对象(Associated Objects)隐式持有实例
如果不想在每个需要使用的类里都添加属性,可以通过objc_setAssociatedObject把实例和一个生命周期足够长的对象(比如当前ViewController、或者UIApplication.shared)绑定,这样既不用显式声明属性,也能给实例加上强引用:
- (void)setupScreenshotListener { CustomOnScreenshot *listener = [[CustomOnScreenshot alloc] init]; [listener subscribeEvent]; // 用当前对象作为宿主,绑定listener实例,使用强引用关联 objc_setAssociatedObject(self, @selector(setupScreenshotListener), listener, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
这里用当前方法的SEL作为关联的key,既避免了重复绑定的冲突,也不用额外定义静态变量当key。当宿主对象(比如当前ViewController)被销毁时,这个关联的CustomOnScreenshot实例也会跟着被释放,不会造成内存泄漏。
2. 将辅助类改为单例模式
如果你的截图监听需求是全局唯一的(整个APP只需要一个监听实例),那直接把CustomOnScreenshot改成单例是最省心的方案。单例会在第一次初始化后一直存活到APP退出,不用任何外部持有:
@implementation CustomOnScreenshot + (instancetype)sharedInstance { static CustomOnScreenshot *_sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sharedInstance = [[self alloc] init]; }); return _sharedInstance; } // 重写allocWithZone防止通过alloc创建新实例 + (instancetype)allocWithZone:(struct _NSZone *)zone { static CustomOnScreenshot *_sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sharedInstance = [super allocWithZone:zone]; }); return _sharedInstance; } @end
使用的时候直接调用:
[[CustomOnScreenshot sharedInstance] subscribeEvent];
这种方式完全不用考虑持有问题,单例自身会维护生命周期。
3. 调整事件订阅的持有逻辑
如果你的subscribeEvent方法是基于自定义的事件中心实现的,可以修改事件中心的逻辑,让它强持有订阅者实例。比如在事件中心里用一个NSMutableArray存储所有订阅者,并且数组对元素是强引用。这样即使外部没有持有CustomOnScreenshot实例,只要事件中心存在,实例就不会被销毁。
如果是监听系统通知(比如UIApplicationUserDidTakeScreenshotNotification),可以用addObserverForName:object:queue:usingBlock:方法,这个方法会返回一个id类型的观察者对象,你只需要把这个对象用关联对象或者其他方式持有即可,不过这种方式和方案1类似,只是持有对象变成了系统通知返回的观察者。
总结
- 全局监听需求:优先选单例模式,最省心且无冗余代码
- 局部页面监听:用关联对象,不用额外加属性就能维持实例生命周期
- 自定义事件系统:调整事件中心的持有逻辑,让事件源强持有订阅者
内容的提问来源于stack exchange,提问作者jegorenkov

