12

I have two NSTimers in my iPhone app. DecreaseTimer works fine, but TimerCountSeconds crashes when I call [timerCountSeconds isValid] or [timerCountSeconds invalidate]. They are used like this:

-(id)initialize { //Gets called, when the app launches and when a UIButton is pressed
 if ([timerCountSeconds isValid]) {
  [timerCountSeconds invalidate];
 } 
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { //Gets called, when you begin touching the screen
 //....
 if ([decreaseTimer isValid]) {
   [decreaseTimer invalidate];
  }
 timerCountSeconds = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(runTimer) userInfo:nil repeats:YES];
 //....
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {//Gets called, when you stop touching the screen(not if you press the UIButton for -(id)initialize)
 //...
 decreaseTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(decrease) userInfo:nil repeats:YES];
 //...
}
-(void)comept3 { //Gets calles when you rubbed the screen a bit
    if ([timerCountSeconds isValid]) {
    [timerCountSeconds invalidate];
    }
}

What did I do wrong? Can you please help me?

esanits
  • 197
  • 1
  • 13
  • have you initialised TimerCountSeconds and DecreaseTimer? – olliej Sep 11 '10 at 19:47
  • I declared them in the header file... I initialised them in "touchesEnded" and "touchesBegan" methods – esanits Sep 11 '10 at 19:51
  • Show more code. Also, having names like `DecreaseTimer` for instance variables is generally a very bad idea. Names that begin with an uppercase letter are used for classes and structs. Consider using a consistent style that Apple advocates. – Stefan Arentz Sep 11 '10 at 19:58
  • okay, I changed the variable names... I forgot to enter the "comept3" method... this will probably help you to help me ;) – esanits Sep 11 '10 at 20:03

6 Answers6

27

You should set an NSTimer object to nil after you invalidate it, since the invalidate method call also does a release (as per the Apple docs). If you don't, calling a method on it like isValid could cause your crash.

Shaggy Frog
  • 27,575
  • 16
  • 91
  • 128
  • 1
    Douple up-vote - searched for this problem for hours... Btw.: It may be *very* sensible to retain the NSTimer if it is checked elsewhere with isValid. If not retained, it may be auto-released when fired (also see the answer from Chuck). – BurninLeo Mar 02 '13 at 20:14
3
 [objTimer retain];

Then it will not crash any time. Use this after initializing the timer so it will work fine....

Natan R.
  • 5,141
  • 1
  • 31
  • 48
jayesh kavathiya
  • 3,531
  • 2
  • 22
  • 25
3

Most likely the timer stored in that variable has already been deallocated. You need to retain it if you want to keep it around for an arbitrarily long time.

Chuck
  • 234,037
  • 30
  • 302
  • 389
  • when will it be deallocated? so when do I have to retain it? – esanits Sep 11 '10 at 20:17
  • It will be deallocated when the runloop decides it can be deallocated. If you want to keep it around, you should retain it when you store it somewhere, as with any other object. – Chuck Sep 11 '10 at 20:28
  • okay I think I understood ;) I retained it after the initialisation... now it does not crash any more:) is that correct? – esanits Sep 11 '10 at 20:33
1

You need to set the timer in main thread. NSTimer will not be fired in background thread.

  • Objc:

    dispatch_async(dispatch_get_main_queue(), ^{
       _timer = [NSTimer scheduledTimerWithTimeInterval:delay target:self selector:@selector(YOUR_METHOD) userInfo:nil repeats:YES];
    });
    
  • Swift:

    dispatch_async(dispatch_get_main_queue()) {
       timer = NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: "YOUR_METHOD", userInfo: nil, repeats: true)
    }
    
zzzz
  • 240
  • 3
  • 6
0

You need to actually initialise the TimerCountSeconds and DecreaseTimer members in initialise. Assuming you're control flow is:

...
myObject = [[MyObject alloc] initialize];
...
[myObject touchesBegan:...]
...
[myObject touchesEnded:...]
...

Then when you call initialize TimerCountSeconds has not been initialised, so you're logically doing

[<random pointer> isValid]

Which will crash. Similarly DecreaseTimer is invalid the first time you call touchesBegan.

In your initialise method you will need to actually initialise everything, before you attempt to use anything.

You also appear to be leaking timers (touchesBegin invalidates the timer but does not release it)

olliej
  • 35,755
  • 9
  • 58
  • 55
0
-(void)StopScanTimer
{
    if(scanTimer != nil)
    {
        [scanTimer invalidate];
        scanTimer = nil;
    }
}

-(void)StartScanTimer
{
    [self StopScanTimer];

    float duration = 10.0f;
    scanTimer = [NSTimer timerWithTimeInterval:duration target:self     selector:@selector(OnScanTimerElapsed) userInfo:nil repeats:NO];
    [[NSRunLoop mainRunLoop] addTimer:scanTimer forMode:NSRunLoopCommonModes];
    [scanTimer retain];
}

-(void) OnScanTimerElapsed
{
    // Do something
}

If you remove "[scanTimer retain];", it will crash when you call invalidate. Always retain the timer if you want to use it back.

ouflak
  • 2,458
  • 10
  • 44
  • 49