Re: Cocoa timer - Using NSTimer or an alternative for timer program
Re: Cocoa timer - Using NSTimer or an alternative for timer program
- Subject: Re: Cocoa timer - Using NSTimer or an alternative for timer program
- From: Alastair Houghton <email@hidden>
- Date: Wed, 14 Dec 2005 11:25:12 +0000
On 14 Dec 2005, at 04:55, Alphonsus Chong wrote:
Hi,
I'm a newbie to Cocoa trying to write a simple timer program. As a
prototype, I used NSTimer to 'tick' at 1 second intervals and that
seemed to work fine. However, when I proceeded to get it to fire at
0.01s, it was wildly inaccurate. A search of the Cocoa
documentation showed that NSTimer has a resolution of 50-100ms.
I have been cracking my head over this and am pretty sure the
answer is pretty simple, but I still can't find it. I would
appreciate it if someone could point me to the right direction or
suggest a good method to get a timer working with 0.01 second
accuracy.
I also read in the same Cocoadev article that runnning NSTimer in a
separate thread would give it a better resolution. I wonder if
anyone could share their opinion/ experience with that, and whether
it would be a good solution.
The thing to realise about timers in event-driven frameworks is that
the only guarantee that they generally provide is that they will not
fire *before* the next time at which they are supposed to. That
being the case, they aren't appropriate for actually timing things;
what they *are* useful for, however, is generating a stream of
regular events.
Since it isn't entirely apparent what the final application for your
timer is, I'll give an example; say I wanted to write a Cocoa app
that worked like a digital stopwatch, and was accurate to (say) a
thousandth of a second. Clearly there's no point updating the
display at that rate (in fact, on Tiger and newer systems, the
framework will throttle your display updates if you try to do that),
so the right approach is to use an NSTimer with a nominal frequency
of (say) 50Hz, and each time it fires, you compute the time
difference since the stopwatch started and display that. e.g.
- (IBAction)startStopwatch:(id)sender
{
start = [NSDate timeIntervalSinceReferenceDate];
/* Invalidate just in case someone triggers this method twice
with no
intervening stopStopwatch: call. */
[timer invalidate];
timer = [NSTimer scheduledTimerWithTimeInterval:0.02
target:self
selector:@selector(tick)
userInfo:NULL
repeats:YES];
}
- (IBAction)stopStopwatch:(id)sender
{
/* One final tick, to get the final value */
[self tick];
/* No need to release, since it's only retained by the run loop */
[timer invalidate];
timer = nil;
}
- (void)tick:(NSTimer *)theTimer
{
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval interval = now - start;
int seconds = (int)interval;
int thousandths = (int)((interval - seconds) * 1000);
[displayedTime setStringValue:
[NSString stringWithFormat:@"d:d:d.d",
(seconds / 3600) % 24,
(seconds / 60) % 60,
seconds % 60,
thousandths]];
}
If you have higher accuracy requirements, then there are some very
fast timers, particularly on PowerPC systems where the CPU contains a
high-resolution timer that runs at anything up to the full core
frequency (depending on the CPU type). Or you might have real-time
requirements (perhaps you're doing audio work?) in which case there
are probably better solutions (I believe Core Audio already sets
things up appropriately, for instance).
One final remark is that if you're doing animation using a timer,
then on each timer tick you should measure the actual elapsed time
(e.g. using NSDate's -timeIntervalSince... methods) and use that to
work out which frame of animation to display (or how far to move
things) rather than just displaying one frame for each timer tick;
that way, you might skip frames, but the animation will look smooth
and the timing will be right.
Kind regards,
Alastair.
p.s. The reason your example doesn't appear to update the text field
is that just updating a text field doesn't ordinarily trigger a
screen refresh until after the application returns to the event
loop... so you'd find that you would only see the last value you
set. Plus there's also the display update throttle that I mentioned
earlier. Anyway, you don't need to run them on separate threads, you
can just do what my example does and return after scheduling an
NSTimer to update the display.
--
http://www.alastairs-place.net
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden