如何知道何时使`NSTimer`无效
这是我的问题:
我有一个模型类,其中有一个NSTimer
,我希望计时器运行模型对象的整个生命周期。 初始化很简单:我只是在init
方法中有以下代码行:
self.maintainConnectionTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(maintainConnection) userInfo:nil repeats:YES];
但是,我的问题是, 当模型从内存中释放时 , 如何使这个计时器无效? 现在,这通常很容易,但是,据我所知,当您计划一个NSTimer
,操作系统维护一个强指针到Timer对象。
我该如何处理? 有没有一种方法在模型从内存中释放之前被调用?
[NSTimer scheduledTimerWithTimeInterval:...]
保留目标 ,所以如果目标是自己的 ,那么你的模型类的实例永远不会被释放。
作为解决方法,可以使用单独的对象(在下面的示例中称为TimerTarget
)。 TimerTarget
有一个对ModelClass
的弱引用,以避免保留周期。
这个“帮手类”看起来像这样。 它唯一的目的是将定时器事件转发给“真正的目标”。
@interface TimerTarget : NSObject @property(weak, nonatomic) id realTarget; @end @implementation TimerTarget - (void)timerFired:(NSTimer*)theTimer { [self.realTarget performSelector:@selector(timerFired:) withObject:theTimer]; } @end
现在,在您的模型类中,您可以创build一个计时器并在dealloc
使其失效:
@interface ModelClass () @property(strong, nonatomic) NSTimer *timer; @end @implementation ModelClass - (id)init { self = [super init]; if (self) { TimerTarget *timerTarget = [[TimerTarget alloc] init]; timerTarget.realTarget = self; self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:timerTarget selector:@selector(timerFired:) userInfo:nil repeats:YES]; } return self; } - (void)dealloc { [self.timer invalidate]; // This releases the TimerTarget as well! NSLog(@"ModelClass dealloc"); } - (void)timerFired:(NSTimer*)theTimer { NSLog(@"Timer fired"); } @end
所以我们有
modelInstance ===> timer ===> timerTarget ---> modelInstance (===> : strong reference, ---> : weak reference)
请注意,从定时器到模型类的实例没有(强)引用。
我用下面的代码testing了它,它创build了ModelClass
一个实例,并在5秒后释放它:
__block ModelClass *modelInstance = [[ModelClass alloc] init]; int64_t delayInSeconds = 5.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ modelInstance = nil; });
输出:
2013-01-23 23:54:11.483 timertest[16576:c07] Timer fired 2013-01-23 23:54:12.483 timertest[16576:c07] Timer fired 2013-01-23 23:54:13.483 timertest[16576:c07] Timer fired 2013-01-23 23:54:14.483 timertest[16576:c07] Timer fired 2013-01-23 23:54:15.483 timertest[16576:c07] Timer fired 2013-01-23 23:54:15.484 timertest[16576:c07] ModelClass dealloc
基于@Martin R的想法,我创build了更易于使用的自定义类,并添加了一些检查以避免崩溃。
@interface EATimerTarget : NSObject // Initialize with block to avoid missing call back - (instancetype)initWithRealTarget:(id)realTarget timerBlock:(void(^)(NSTimer *))block; // For NSTimer @selector() parameter - (void)timerFired:(NSTimer *)timer; @end @interface EATimerTarget () @property (weak, nonatomic) id realTarget; @property (nonatomic, copy) void (^timerBlock)(NSTimer *); // use 'copy' to avoid retain counting @end @implementation EATimerTarget - (instancetype)initWithRealTarget:(id)realTarget timerBlock:(void (^)(NSTimer *))block { self = [super init]; if (self) { self.realTarget = realTarget; self.timerBlock = block; } return self; } - (void)timerFired:(NSTimer *)timer { // Avoid memory leak, timer still run while our real target is dealloc if (self.realTarget) { self.timerBlock(timer); } else { [timer invalidate]; } } @end
这是我的样本class
@interface MyClass @property (nonatomic, strong) NSTimer *timer; @end @implementation MyClass - (id)init { self = [super init]; if (self) { // Using __weak for avoiding retain cycles __weak typeof(self) wSelf = self; EATimerTarget *timerTarget = [[EATimerTarget alloc] initWithRealTarget:self timerBlock: ^(NSTimer *timer) { [wSelf onTimerTick:timer]; }]; self.timer = [NSTimer timerWithTimeInterval:1 target:timerTarget selector:@selector(timerFired:) userInfo:nil repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode]; } return self; } - (void)dealloc { [self.timer invalidate]; // This releases the EATimerTarget as well! NSLog(@"### MyClass dealloc"); } - (void)onTimerTick:(NSTimer *)timer { // DO YOUR STUFF! NSLog(@"### TIMER TICK"); } @end