Ilya Suzdalnitski
Ilya Suzdalnitski

Reputation: 53540

NSTimer doesn't stop

I've got a timer which fires when the viewWillAppear method is being called and invalidates when the 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");
}

Upvotes: 23

Views: 25016

Answers (12)

Pau Ballada
Pau Ballada

Reputation: 1628

This did the trick to me in Swift

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)

    dispatch_async(dispatch_get_main_queue(),{ [weak self] in

        self?.idleTimer?.invalidate()
        self?.idleTimer = nil

    })

}

Upvotes: 1

Habib Ali
Habib Ali

Reputation: 280

This works for me

dispatch_async(dispatch_get_main_queue(), ^{
    [timer invalidate];
});

Upvotes: 0

Terry
Terry

Reputation: 339

After creating your timer just register it to run in NSRunLoopCommonModes:

[[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSRunLoopCommonModes];

Then invalidate method will work.

Upvotes: 0

Matjan
Matjan

Reputation: 3607

- (void)viewWillAppear:(BOOL)animated gets called again.

In fact it gets called every time you switch views or after you dismiss a modal view. Basically any time your view actually reappears on screen, not just the first time.

So in this case the timer gets restarted, even though is was invalidated before.

Upvotes: 0

Mona
Mona

Reputation: 6175

This is an absolute solution. None of the ones above worked for me, so I did this,

where you want to start the timer,

[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:NO];

The method that the timer calls,

-(void)timerMethod
{
    // do what you need to do

    if (needs to be called again) {

        [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:NO];        
    }
}

hope this helps,

Upvotes: 14

9to5ios
9to5ios

Reputation: 5545

**In .h class**

@interface 
{
NSTimer * theTimer;

}


**in .m class**

//put this code where you want to calling the NSTimer function

        theTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(updateCounter:) userInfo:nil repeats:YES];



- (void)updateCounter:(NSTimer *)theTimer 
{

   // write your code here for counter update 

}


// put this code to Stop timer:

    [theTimer invalidate];
    theTimer = nil;

Upvotes: 1

Bruno
Bruno

Reputation: 73

I had the same problem.

I can't dealloc my instance if the timer is still running, so I have to stop it before calling :

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

After calling this method my timer really stop

Upvotes: 0

Ilya Suzdalnitski
Ilya Suzdalnitski

Reputation: 53540

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];

Upvotes: 38

Ben Gotow
Ben Gotow

Reputation: 14894

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!

Upvotes: 2

lostInTransit
lostInTransit

Reputation: 70997

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

Upvotes: 6

Gordon Childs
Gordon Childs

Reputation: 36074

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.

Upvotes: 0

rpetrich
rpetrich

Reputation: 32316

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...

Upvotes: 0

Related Questions