views:

3090

answers:

5

Hi,

I've got a timer which fires when viewWillAppear method is being called and invalidates when viewDidDisappear method is being called. But after a certain amount of switching between views the timer continues firing even after it was invalidated. What's the problem?

Here is my code:

NSTimer *timer;

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    timer = [NSTimer scheduledTimerWithTimeInterval: 0.2f
              target: self
                selector:@selector( timerAction )
                userInfo:nil
              repeats:YES];

}

-(void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [timer invalidate];
    timer = nil;
}

-(void) timerAction
{
    NSLog(@"timerAction");
}
A: 

You have forgotten to release the timer in viewWillDisappear:

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
    [timer invalidate];
    [timer release];
    timer = nil;
}

This shouldn't cause the timer to keep firing though...

rpetrich
In this case app crushes because timer is already being released.
Ilya
Ben Gotow has the right answer here. You must retain the timer after it's created, and then invalidate and release it when you want it to stop calling you back
rpetrich
You don't /have/ to retain it, since the run loop will do that, but it's a good idea if you want to be safe.
Wevah
A: 

Of invalidate the doco says

"The run loop removes and releases the timer, either just before the invalidate method returns or at some later point."

I suppose yours is getting removed at some later point.

Rhythmic Fistman
+2  A: 

A method called by a timer should have the definition

- (void)methodName:(NSTimer *)aTimer;

This way the method has the instance of timer which was fired. The way you are doing it, the method will not know whether the timer was invalidated or not.

Try changing your timer initialization to

timer = [NSTimer scheduledTimerWithTimeInterval: 0.2f target: self selector:@selector(timerAction:) userInfo:nil repeats:YES]

and the method to

-(void) timerAction:(NSTimer *)aTimer{...}

Hope that helps

lostInTransit
This won't actually have any effect.
rpetrich
+1  A: 

This may not be the issue, but you need to retain the reference to timer that is returned from scheduledTimerWithInterval:. Without doing this, your pointer to the timer might be invalid by the time you go to stop it.

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    timer = [[NSTimer scheduledTimerWithTimeInterval:0.2f target:self selector:@selector(timerAction) userInfo:nil repeats:YES] retain];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [timer invalidate];
    [timer release];
    timer = nil;
    [super viewDidDisappear:animated];
}

- (void)dealloc
{
    [timer invalidate];
    [timer release];
    timer = nil;
    [super dealloc];
}

Also, try setting a breakpoint in viewDidDisappear and make sure it's getting called!

Ben Gotow
+4  A: 

I should have been keeping a link to the timer by retaining it. :) I've asked this question 5 months ago, and it's amazing, how much more experience I acquired. :)

timer = [ [NSTimer scheduledTimerWithTimeInterval: ...] retain];
...
...
[timer invalidate];
[timer release];
Ilya