venerdì 4 marzo 2011

How to execute a NSTimer from NSThread

Few days ago I run into a problem related to user interaction on UIPickerView and a NSTimer. When the user interact with UIPickerView the NSTimer is delayed. That is obvious but really annoying.
I found a partial solution Naren blog, but unfortunately it is incomplete. I would like to stop and restart the NSTimer many times and with Naren's code there is a memory leak (NSThread).

The the final solution is the following:


@interface Test : NSObject
{
     NSTimer *_timer;
     NSRunLoop *_runLoop;
     NSThread *_timerThread;
}

- (void)suspendTimer;
- (void)resumeTimer;
- (void)timerFireMethod:(NSTimer*)timer;

@end // End Test interface


@implementation Test

- (void)suspendTimer
{
     if(_timer != nil)
     {
          [_timer invalidate];
          [_timer release];
          _timer = nil;

          CFRunLoopStop([_runLoop getCFRunLoop]);

          [_timerThread release];
          _timerThread = nil;
     }
}

- (void)_startTimerThread
{
     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

     _runLoop = [NSRunLoop currentRunLoop];
     _timer = [[NSTimer scheduledTimerWithTimeInterval:1.0
          target:self
          selector:@selector(timerFireMethod:)
          userInfo:nil
          repeats:YES] retain];
     [_runLoop run];
     [pool release];
}

- (void)resumeTimer
{
     if(_timer == nil)
     {
          _timerThread = [[NSThread alloc] initWithTarget:self
               selector:@selector(_startTimerThread)
               object:nil];
          [_timerThread start];
     }
}

- (void)timerFireMethod:(NSTimer*)timer
{
     // ...
}

@end // End Test implementation


For some strange reason NSRunLoop class doesn't provide an API to stop the run loop. The trick is to convert NSRunLoop into CFRunLoop and stop it.

In NSRunLoop class description is specified that the methods are not thread safe. I didn't experienced any problems with this implementation. As alternative the code above can be completely written using CFRunLoop type and functions:

  • [NSRunLoop currentRunLoop] -> CFRunLoopCurrent()
  • NSRunLoop * -> CFRunLoopRef
In CFRunLoop documentation I didn't found any thread safety warning.

Enjoy !

--
boydon

2 commenti:

  1. Thank you, I had an issue in updating the audio player's duration (through UISlider in UITableViewCell). You code resolved the issue and saved time.

    RispondiElimina
  2. Se cerchi un prodotto eccezionale... devi andare in un posto eccezionale. Scopri il test. Vai su http://www.vitalbios.com/A/MTQ3MDg5NzQzMSwxMDAwMDA0LHRlc3QuaHRtbCwyMDE2MDkwOCxvaw==

    RispondiElimina