Is nstimer inaccurate?

I use NSTimer to update a label that shows the current time every minute. I start the timer for the full minute date (e.g. 2:23:00 instead of 2:22:17). The problem is that NSTimer does not work accurately, which means that it sometimes works too early (which leads to (for example) 2:23 pm, when it really is 2:24 pm).

Is there a fix / workaround?

EDIT: Here, as I start the timer:

 NSTimer *clockTimer = [[NSTimer alloc] initWithFireDate:[NSDate nextFullMinuteDate] interval:60.0 target:self selector:@selector(updateClocks:) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:clockTimer forMode:NSDefaultRunLoopMode]; [clockTimer release]; 

Btw, I tried to add a few milliseconds to the fire date, this seems to help, but it seems like finding a suitable interval to add is a miss.

EDIT 2: Okay, so I manually set my timer to fire 0.05 seconds after a full minute, and then again after 60.0 seconds. Now here is what I registered in the called method:

 0.048728 0.047153 0.046484 0.044883 0.043103 

As you can see, NSTimer does not actually use the 60 second time interval, but a little shorter.

+4
source share
4 answers

Well, I just wrote my little NSTimer shell, called MinuteTimer , to "solve" the problem. I uploaded it to pastebin . The idea is to reset the timer whenever it shoots too fast, so often that the wrong date is displayed incorrectly.

Since this is channel-tape programming, I still want to hear other answers!

0
source

From NSTimer docs :

A timer is not a real-time mechanism; it works only when one of the launch cycle modes to which the timer has been added is working and can check if the timers have passed. Due to different input sources, they are controlled by a typical trigger cycle, the effective resolution of the time interval for the timer is limited to about 50-100 milliseconds. If the response time of the timers occurs during a long callout or while the start-up cycle is in a mode that does not control the timer, the timer does not work until the next timer check cycle. Therefore, the actual time during which the timer is triggered can be a significant amount of time after the planned firing time.

This means that NSTimer is terribly inaccurate.

If you program a β€œstopwatch,” you can use NSTimer to display the results for the user, and NIRTimer (one of my favorite classes) keeps track of time. This is described below:

 NIRTimer *nirTimer; NSTimer *timer; - (void)start { nirTimer = [[NIRTimer alloc]init]; timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES]; [nirTimer start]; - (void)timerFired:(NSTimer *)timer { [self displayTimeToUser:[nirTimer seconds]]; } 

In the above code, NSTimer may be inaccurate, but the exact time is stored in NIRTimer , which is displayed to the user 0.01 seconds to the user.

If you really need a specific code run at a specific time, the best solution I have found is to use NSThread and usleep() :

 BOOL timerRunning; - (void)start { timerRunning = YES; [NSThread detachNewThreadSelector:@selector(timer) toTarget:self withObject:nil]; //make a new thread } - (void)timer { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init]; //Make an autorelease pool since we're in a new thread NIRTimer *timer = [[NIRTimer alloc]init]; //Make an NIRTimer while (timerRunning) { [timer reset]; [timer start]; [self doSomething]; usleep(10000 - [timer microseconds]); //Wait for .01 seconds which is 10000 microseconds (a microsecond is 1/1000000th of a second) - the time it took to accomplish whatever we were doing } //Clean up [timer release]; [pool release]; } 

In this case, set timerRunning to NO to stop the timer. You can also use [self performSelectorOnMainThread:@selector(doSomething) withObject:nil waitUntilDone:NO] instead of [self doSomething] if what you are trying to do is not thread safe.

For more on the beautiful world of streaming, see the Stream Programming Guide .

+1
source

I assume that you start from the current time, and each time your NSTimer shuts down, you add one minute to this initial time and then display it. What you should do with your timer: every time the timer goes off, re-read the current time and just show it.

0
source

You must set a timer for every second instead of every minute. But do not update your shortcut every second. Just compare if the new minutes in the current time differ, then the minutes for the last time you updated the label, and if so, update the label.

0
source

All Articles