2

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.

RK-
  • 12,099
  • 23
  • 89
  • 155
LuckyLuke
  • 47,771
  • 85
  • 270
  • 434
  • how did your clock go? I have a similar problem that I need to monitor a URL every 2 seconds. I wonder what solution did you use. leo.hou@gmail.com – leo Sep 15 '11 at 07:43

2 Answers2

4

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];
}
Jim Dovey
  • 11,166
  • 2
  • 33
  • 40
  • But what happens when the user holds e.g. a cell? – LuckyLuke Feb 09 '11 at 20:02
  • Also install the timer on NSEventTrackingRunLoopMode or UIEventTrackingRunLoopMode so it'll still fire while input event tracking occurs. – Jim Dovey Feb 09 '11 at 21:57
  • I do not find a constant like that. – LuckyLuke Feb 10 '11 at 09:31
  • Sorry, was working from memory. On iOS, it's called `UITrackingRunLoopMode`, declared near the bottom of `UIApplication.h`. On Mac, it's called `NSEventTrackingRunLoopMode`, declared near the top of `NSApplication.h`. There's also `NSModalPanelRunLoopMode` there, used for application-modal alerts. – Jim Dovey Feb 10 '11 at 19:29
1

No need to run the timer on a secondary thread, just create the timer on the main thread. You must not update the GUI from a secondary thread, yes you could use performSelectorInMainThread but why bother? Just put the whole thing on the main thread, as long as your timer isn't called "too often," performance will be fine.

Bogatyr
  • 19,255
  • 7
  • 59
  • 72
  • If I put the timer in main thread(updates every second since it is a clock) and the user blocks the runloop with a touch e.g then the clock stops. – LuckyLuke Feb 09 '11 at 19:44