37

In ios5.0 with ARC, in my rootviewcontroller I call a method in a security manager object that is held by the app delegate. In that method I setup the timer as below

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self 
                                       selector:@selector(updateModel:) userInfo:str repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 

However, this never fires the selector ie. updateModel: never gets called. What may be wrong? Is there another more efficient way I can do this without using NStimer?

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
inforeqd
  • 3,209
  • 6
  • 32
  • 46

4 Answers4

130

Could also be a threading problem:

if

[NSThread isMainThread]

is false then start the timer like this:

dispatch_async(dispatch_get_main_queue(), ^{
        timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(tick:) userInfo:nil repeats:YES];
    })
tmanthey
  • 4,547
  • 6
  • 35
  • 42
  • 6
    The timer executes only in main (UI) thread correctly. If you try to start the timer in another thread than the main thread it won't fire. – tmanthey Feb 26 '15 at 08:00
  • 3
    The 'scheduledTimer' methods try to use the runloop associated with the current thread. The main thread always has a runloop, but other threads don't have one unless you create it explicitly. In that case the timer isn't scheduled. – Greg Ball Mar 25 '18 at 20:41
16

You seem to be a bit mixed up with your timer variable.

You initialize a new timer but you aren't actually using it. Do you want to use the timer you initialized or do you want to you ApplicationDelegate.timer?

Here are the two possible solutions.

Option One (assuming that you have a class instance titled ApplicationDelegate and that it has a timer property):

ApplicationDelegate.timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(updateModel:) userInfo:str repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:ApplicationDelegate.timer forMode:NSRunLoopCommonModes];

Option Two:

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(updateModel:) userInfo:str repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
sosborn
  • 14,676
  • 2
  • 42
  • 46
  • sorry about the mistake in the post.. I've edited it to reflect the way I'm doing it. I'm using the second way you mentioned... It's still not working. – inforeqd Mar 29 '12 at 02:56
  • 24
    The second way is incorrect. It is attempting to add the timer twice. `scheduledTimerWithTimeInterval:...` has already added the timer. Do make sure that you're running this on the main thread. – Rob Napier Mar 29 '12 at 02:59
  • is there a way to find out in which thread the timer is being added to? I think it is the main thread that I'm adding it to... – inforeqd Mar 29 '12 at 03:03
  • 1
    NSAssert([NSThread isMainThread], @"Must run on the main thread"); – Rob Napier Mar 29 '12 at 03:14
  • 1
    thanks a million.. This is what I used and now the selector is firing. :-) pls see if I've got it right. Would blocks or GCD be better? if (![NSThread isMainThread]) { [self performSelectorOnMainThread:@selector(selectorToRunInMainThread) withObject:nil waitUntilDone:NO]; } -(void)selectorToRunInMainThread{ NSString *str = @"Timer event has fired"; NSTimer *atimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateModel:) userInfo:str repeats:YES]; } – inforeqd Mar 29 '12 at 03:15
  • I was having a problem with the timer not firing using the option two, but it wasnt firing because I have a [timer invalidate] before. Just giving some extra info. – vrunoa Jan 12 '15 at 23:42
9

I catch the same issue and I fire timer in main queue to solve it:

[NSURLConnection sendAsynchronousRequest:request queue:_operationQueue
    completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
         [self loopUpUpdateStart];
}];

-(void)loopUpUpdateStart{
    dispatch_async(dispatch_get_main_queue(), ^{

        _loopTimerForUpRevision = 
          NSTimer scheduledTimerWithTimeInterval: kNetworkLoopIntervalUpRev
                                          target: self
                                        selector: @selector(myCoolMethod)
                                        userInfo: nil
                                         repeats: YES];
        TRACE(@"Start Up updates");
    });
}
REALFREE
  • 4,378
  • 7
  • 40
  • 73
WINSergey
  • 1,977
  • 27
  • 39
7

This line has several problems:

[[NSRunLoop currentRunLoop] addTimer:ApplicationDelegate.timer forMode:NSRunLoopCommonModes]; 

First, it should not be required at all. -scheduledTimerWithTimeInterval:... already adds the timer to the runloop. You do not need to add it again.

Second, the local variable timer is unrelated to the property ApplicationDelegate.timer (which is presumably nil at this point).

If you're talking to the application delegate so much that you've created something called ApplicationDelegate (a global? a macro?), you're talking to it too much. The application delegate is the delegate for the application; it assists in the application starting and stopping and responding to system events. The application delegate is not a place to store global variables. A timer is definitely not the kind of thing you'd fetch from another object in any case.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • sorry about the mistake in the post.. I've edited it to reflect the way I'm doing it.... It's still not working. About your comment on using the Application delegate.. I have a model that I create after parsing a web service response. I have to retain the model for all the views in the application. I therefore think the model should be held by the application delegate otherwise, am not sure where else can i keep it for all the views to use. – inforeqd Mar 29 '12 at 02:55
  • 1
    You can either pass the model to the view controllers when you construct them, or you can put your model objects in a singleton. You should not hang them on the app delegate. This makes code reuse very difficult, and complicates the app delegate (which has its own function to perform unrelated to storing data). – Rob Napier Mar 29 '12 at 02:58