You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

【技术求助】辅助类对象初始化后立即销毁,存属性可用却嫌冗余

解决方案:避免手动持有辅助类实例却能防止其被立即销毁

这是个非常典型的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:08:29