如何知道何时使`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