Nosrettap Nosrettap - 1 year ago 104
iOS Question

How to know when to invalidate an `NSTimer`

Here's my issue:

I have a model class that has an

in it that I want the Timer to run for the entire lifespan of the model object. Initiliazation is easy: I just have the following line of code in the

self.maintainConnectionTimer =
[NSTimer scheduledTimerWithTimeInterval:1

However, my issue is, how do I invalidate this timer when the model is released from memory? Now, this would usually be easy, however, as far as I know, when you schedule an
the OS maintains a strong pointer to the Timer object.

How should I deal with this? Is there a method that gets called right before the model is released from memory?

Answer Source

The [NSTimer scheduledTimerWithTimeInterval:...] retains the target, so if the target is self, then your instance of the model class will never be deallocated.

As a workaround, one can use a separate object (called TimerTarget in the following example). TimerTarget has a weak reference to ModelClass, to avoid a retain cycle.

This "helper class" looks like this. Its only purpose is to forward the timer event to the "real target".

@interface TimerTarget : NSObject
@property(weak, nonatomic) id realTarget;

@implementation TimerTarget

- (void)timerFired:(NSTimer*)theTimer
    [self.realTarget performSelector:@selector(timerFired:) withObject:theTimer];


Now, in your model class, you can create a timer and invalidate it in dealloc:

@interface ModelClass ()
@property(strong, nonatomic) NSTimer *timer;

@implementation ModelClass

- (id)init
    self = [super init];
    if (self) {
        TimerTarget *timerTarget = [[TimerTarget alloc] init];
        timerTarget.realTarget = self;
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1
                                               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");


So we have

modelInstance ===> timer ===> timerTarget ---> modelInstance
(===> : strong reference, ---> : weak reference)

Note that there is no (strong) reference from the timer to the instance of the model class anymore.

I have tested this with the following code, which creates an instance of ModelClass and releases it after 5 seconds:

__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
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download