28
May/09
8

Running NSTimer on Separate Thread

I’ve been working in something requiring an NTimer to fire every second. The documentation says that an NSTimer needs to be attached to a NSRunLoop to fire. So I started off with the following:

NSTimer* timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerTick:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode];

So, what I am doing here is creating a new NSTimer which fires in 1 second intervals and sends the timerTick message to self every time it fires. On the 2nd line, I am attaching the NSTimer object to the current runloop. This all worked fine. Until…!

It turns out in UI applications, the NSRunLoop is responsible for getting input from all the input devices/timers that are attached to it. The timer would fire and the tickerTick message got sent as expected until the runloop got busy doing something else. For example if I pressed a button on the UI and kept it pressed, the runloop was busy reading the mouse down input and the timer ticks would get ignored completely.

So, the solution was to run the NSTimer in a separate thread so it can have a NSRunLoop dedicated to responding to it and nothing else.

Here is the code:

-(void) startTimer
{
NSThread* timerThread = [[NSThread alloc] initWithTarget:self selector:@selector(startTimerThread) object:nil]; //Create a new thread
[timerThread start]; //start the thread
}

//the thread starts by sending this message
-(void) startTimerThread
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
[[NSTimer scheduledTimerWithTimeInterval: 1.0
target: self
selector: @selector(timerTick:)
userInfo: nil
repeats: YES] retain];

[runLoop run];
[pool release];
}

- (void)timerTick:(NSTimer *)timer
{
NSLog(@"Tick!");
}

The NSRunLoop for the new thread needs to be explicitly set to run. If this is not done, the thread will start the timer, and then duly exit the function (and therefore the thread) and no ticks will be received. Also, the NSAutoreleasePool object needs to be created to manage the objects that are created in the new thread.

Cheers :)

Comments (8) Trackbacks (0)
  1. Handi
    3:14 am on July 2nd, 2009

    Hi Naren,

    Thank you for writing this example.
    I did the same thing but I ran into a leak problem on the runloop.
    I ran “Leak” performance tool on my app. And it always pointing out that I have a 16 bytes leak on [runloop run].
    This leak only happens after I have the timer finish running meaning the timer got invalidated -> [pool release] got called.

    I wonder if you are having the same issue as well.

    I read in the documentation that NSRunLoop should only be called within the context of current thread. I wonder if this is causing the leak…

    Handi

  2. narendra.tumkur
    4:32 am on July 2nd, 2009

    Hi Handi,
    I am not able to check this immediately, but is it possible that you need to do a [NSTimer release] before [pool release]?

    Naren

  3. Handi
    3:02 pm on July 2nd, 2009

    Hi Naren,

    I just tried that too and it is still leaking the same thing.
    This is what being shown in the tool:
    Leaked Object # Address Size Responsible Library Responsible Frame
    Malloc 16 Bytes 0×3a19f70 16 WebCore CurrentThreadContext()

    Thx for such prompt reply!

    Handi

  4. narendra.tumkur
    2:15 pm on July 5th, 2009

    Hey Handi,
    I’m not sure why there is a leak. I haven’t had time to try it out myself.
    You can however find out which object has not been released with the Leak performance tool.
    Sorry I can’t be of much help right now :|

    Cheers
    Naren

  5. rebs
    11:22 pm on July 26th, 2009

    Hey just wanted to say thanks, I was hitting my head against the brick wall with the problem of the thread exiting.

    the [runLoop run] fixed it!.

  6. narendra.tumkur
    6:58 am on July 27th, 2009

    No problem. Glad I could help.

  7. Rorun
    4:10 pm on August 27th, 2009

    Hi!, I newbie in the manage of threads in COCOA. In the example, How kill the thread? if I need use many threads, and i need kill each one of them. What is the correct way for manipulate them.

    Thanks.

  8. Hasnat
    11:22 am on November 23rd, 2009

    incase you are wondering how to stop it
    just use
    [timer invalidate];
    in timerTick:

Leave a comment

No trackbacks yet.