10

I have a code snippet like this:

m_timer = [NSTimer scheduledTimerWithTimeInterval:timeOutInSeconds
                                                       target:self
                                                     selector:@selector(activityIndicatorTimer:)
                                                     userInfo:nil
                                                      repeats:NO];

When I call it like so the selector is not fired after the given timeOutInSeconds. However, if I modify it to be like the following, then the selector is called twice.

NSLog(@"Timer set");
m_timer = [NSTimer scheduledTimerWithTimeInterval:timeOutInSeconds
                                               target:self
                                             selector:@selector(activityIndicatorTimer:)
                                             userInfo:nil
                                              repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:m_timer forMode:NSRunLoopCommonModes];

Could anyone offer any suggestion as to what I am likely doing wrong?

I am using XCode 5.1, and building on 7.1.1 iPhone 4S

xceph
  • 1,036
  • 2
  • 13
  • 28
  • 6
    My guess is you're spawning this timer off a background thread without a runloop (most don't have one) `scheduledTimerWithTimeInterval` must be called from a thread with a runloop (usually the main thread). To add the timer to runloop as you are, you should be using `timerWithTimeInterval...` instead. – David Berry May 12 '14 at 15:40
  • Call `[m_timer fire]` after the first option – Midhun MP May 12 '14 at 15:53
  • Thank you David, I believe you were correct. With your help it seems I have found the issue. If you wish to submit this as a answer, rather than comment, I can accept it. – xceph May 12 '14 at 15:56
  • xceph You should mark @nmh answer as correct to help others find the solution faster. – GoRoS Apr 13 '16 at 06:49

2 Answers2

39

Call this timer in main thread:

dispatch_async(dispatch_get_main_queue(), ^{
   m_timer = [NSTimer scheduledTimerWithTimeInterval:timeOutInSeconds
                                                   target:self
                                                 selector:@selector(activityIndicatorTimer:)
                                                 userInfo:nil
                                                  repeats:NO];
});
nmh
  • 2,497
  • 1
  • 15
  • 27
3

You have 3 options in creating timer, as Apple states in the doc:

  • Use the scheduledTimerWithTimeInterval:invocation:repeats: or scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: class method to create the timer and schedule it on the current run loop in the default mode.
  • Use the timerWithTimeInterval:invocation:repeats: or timerWithTimeInterval:target:selector:userInfo:repeats: class method to create the timer object without scheduling it on a run loop. (After creating it, you must add the timer to a run loop manually by calling the addTimer:forMode: method of the corresponding NSRunLoop object.)
  • Allocate the timer and initialize it using the initWithFireDate:interval:target:selector:userInfo:repeats: method. (After creating it, you must add the timer to a run loop manually by calling the addTimer:forMode: method of the corresponding NSRunLoop object.)

The method you are using already schedules the timer on the current loop and you should not schedule another time. In my opinion the problem is elsewhere, try (to make it easy) to put a fixed value instead of timeOutInSeconds.
Also the most common way to call something after a specific delay (that should not repeat) is use dispatch_after:

 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        //YOUR CODE
    });

Where the 2 is an arbitrary interval (in this case 2 seconds).

Andrea
  • 26,120
  • 10
  • 85
  • 131