Search code examples
iphonecocoa-touchnstimernsthread

Thread and NSTimer


I am making an application that has a timer. I count the minutes and seconds from a given time down to 0. When this happen I launch an alertview.

My structure is this:

Mainthread method allocate a new thread and initialize it. The entrypoint(method) for the thread has a timer which invokes a method for calculating time left, and if the time is up, displays an alertview.

However, is this correct? Because now I am updating GUI from another thread than the main...and that is bad right? And I am also displaying an alertview from this thread.

I thought of making another method that encapsulate all the logic for updating and displaying the alertview and in the method that the nstimer invokes use the performSelectorInMainThread, however is this correct?

Thanks for your time.


Solution

  • Assuming that it's fairly simple to determine how much time is left, just run your timer on the main thread. The timer gets attached to the current runloop, so it's not blocking anywhere, and its callback method shouldn't take an inordinate amount of time to run, and can therefore update the UI nicely.

    - (void) initializeTimerWithEndTime: (NSDate *) endTime
    {
        // call this on the main thread & it'll automatically
        // install the timer on the main runloop for you
        self.countdownTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0
                                                               target: self
                                                             selector: @selector(timerTick:)
                                                             userInfo: endTime
                                                              repeats: YES];
    #if __TARGET_OS_IPHONE__
        // fire while tracking touches
        [[NSRunLoop mainRunLoop] addTimer: self.countdownTimer
                                  forMode: UITrackingRunLoopMode];
    #else
        // fire while tracking mouse events
        [[NSRunLoop mainRunLoop] addTimer: self.countdownTimer
                                  forMode: NSEventTrackingRunLoopMode];
        // fire while showing application-modal panels/alerts
        [[NSRunLoop mainRunLoop] addTimer: self.countdownTimer
                                  forMode: NSModalPanelRunLoopMode];
    #endif
    }
    
    - (void) cancelCountdown
    {
        [self.countdownTimer invalidate];
        self.countdownTimer = nil;
    }
    
    - (void) timerTick: (NSTimer *) aTimer
    {
        NSDate * endDate = [timer userInfo];
        NSDate * now = [NSDate date];
    
        // have we passed the end date?
        if ( [endDate laterDate: now] == now )
        {
            // show alert
            [self cancelCountdown];
            return;
        }
    
        // otherwise, compute units & show those
        NSUInteger units = NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit;
    
        NSDateComponents * comps = [[NSCalendar currentCalendar] components: units
                                                                   fromDate: [NSDate date]
                                                                     toDate: endDate
                                                                    options: 0];
        [self.clockView setHours: comps.hour
                         minutes: comps.minute
                         seconds: comps.second];
    }