我的应用程序中有一个单例类,它是在应用程序启动时创建的并且始终在使用中。我现在要介绍一个定期调用单例方法之一的NSTimer,所以我的理解是计时器将保留对我的单例类的强引用(因为单例类是目标)。它是否正确?更重要的是,一个强大的保留周期是一个单身类的问题,应该在应用程序的持续时间内生存?如果是这样,为什么?
答案 0 :(得分:3)
你问:
我的应用程序中有一个单例类,它是在应用程序启动时创建的并且始终在使用中。我现在要介绍一个定期调用单例方法之一的
NSTimer
,所以我的理解是计时器将保留对我的单例类的强引用(因为单例类是目标)。它是否正确?
是的,NSTimer
将保持对单例类的实例的强引用。这个强引用会一直保持到invalidate
计时器(或者在非重复计时器的情况下,直到计时器触发)。
更重要的是,一个强大的保留周期是一个单身类的问题,应该在应用程序的持续时间内生存?如果是这样,为什么?
预定NSTimer
实例维护对其处理程序目标的强引用这一事实本身并不成问题。你只需要了解这个事实并妥善管理你的记忆。所有这些意味着如果你完成了某个NSTimer
目标的对象,你就要负责手动调用该计时器的invalidate
。
对计时器处理程序的这种强引用变得有问题的唯一一次是当你处理一些具有更短暂性质的对象时(例如,后来可能被解雇的视图控制器)。人们常常认为“哦,我只会invalidate
NSTimer
dealloc
dealloc
,但这不起作用。在没有更多强引用之前,不会调用Objective-C deinit
方法(以及Swift dealloc
方法),因此您无法尝试在{{1}中解析该计时器的强引用因为dealloc
在定时器失效之前无法被调用。相反,如果您已完成NSTimer
的目标,则必须手动invalidate
计时器(例如,在视图控制器示例的情况下,人们经常在viewDidDisappear
中执行此操作)。
在你的情况下,你有一个预定的重复计时器,调用单例方法不是问题。请注意,只要预定的NSTimer
仍处于活动状态,就无法取消分配此单例对象(可能永远不会超出范围)。您必须invalidate
NSTimer
才能解决计时器对该目标的强引用。
另外两个观察结果:
您已将此NSTimer
行为描述为强参考周期(保留周期)。从技术上讲,这不是一个强大的参考周期,而是一个事实,即调度NSTimer
由其调度的运行循环保留,然后计时器保留计划其处理程序的目标。
这有点学术上的区别(因为它表现得很像一个强大的参考周期),但值得注意。
您还应该知道,您也可以使用GCD调度源计时器,而不是使用NSTimer
。因此,在类中创建一个timer属性:
@property (nonatomic, strong) dispatch_source_t timer;
然后实例化并启动计时器:
typeof(self) __weak weakSelf = self;
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(self.timer, ^{
[weakSelf handleTimer];
});
dispatch_resume(self.timer);
与NSTimer
方法不同,此模式不会保留计时器的目标(或者,在这种情况下,我们通过self
引用的对象),因为使用了块中的weakSelf
模式(解决任何强引用周期)。因此,当包含它的对象被释放时,我们最终会进入一个安全释放的计时器,没有强大的参考周期。
答案 1 :(得分:1)
我认为这是一个问题。单例类已经难以重构和重用。将保留周期添加到一个将使得该类在将来更难以更改或重用。当你发现你需要这个类(或至少是这个计时器所属的一组职责)不是单身时,你还需要记住或识别这个内存泄漏并将其作为重构的一部分进行修复。未来你可能会伤心。
相反,您可以编写一个遵循良好模式(用于内存管理和其他方式)的类,无论它是否作为单例访问。在您的情况下,可能意味着保留对计时器的弱引用(“Note in particular that run loops maintain strong references to their timers, so you don’t have to maintain your own strong reference to a timer after you have added it to a run loop.”)并在取消分配此对象时使该计时器失效。
答案 2 :(得分:0)
此时保留周期可能没有问题,因为您正在使用不需要重新分配的单例,但如果将来其他人将单例重构为常规类会怎么样?
另一个考虑因素是NSTimer
不是非常节能,Apple warmly recommends切换到GCD
来源执行与计时器相关的任务。您甚至可以使用此方法通过weak
引用中断保留周期。