NSTimer不准确?

时间:2011-10-06 17:33:20

标签: cocoa nstimer

我正在使用NSTimer更新标签,以显示每分钟的当前时间。我在完整的分钟日期(例如2:23:00而不是2:22:17)开启计时器。问题是NSTimer似乎工作不准确,这意味着它有时会过早发生(导致(例如)下午2:23,当它真的是下午2:24)。

是否有任何修复/解决方法?

编辑:以下是我启动计时器的方法:

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];

顺便说一句,我已经尝试在火灾日期添加几毫秒,这似乎有所帮助,但看起来找到一个合适的间隔来添加就是命中注定。

编辑2:好的,所以我手动设置我的计时器在一整分钟后开始0.05秒,然后在60.0秒后再次开始。现在这是我在被调用方法中登录的内容:

0.048728
0.047153
0.046484
0.044883
0.043103

正如您所看到的,NSTimer实际上并不使用60秒的时间间隔,而是稍微短一些。

4 个答案:

答案 0 :(得分:1)

来自NSTimer docs

  

计时器不是实时机制;它只在其中一个发射时发射   已添加计时器的运行循环模式正在运行且能够运行   检查计时器的开火时间是否已经过去。因为各种各样   输入源一个典型的运行循环管理,有效的分辨率   计时器的时间间隔限制在50-100的量级   毫秒。如果在长时间标注期间发生计时器的发射时间或   当运行循环处于不监视计时器的模式时,   在下次运行循环检查计时器之前,计时器不会触发。   因此,计时器可能发射的实际时间可能是   在计划的射击时间之后很长一段时间。

这意味着NSTimer非常不准确。

如果你编写一个"秒表,"您可以使用NSTimer向用户显示结果,而NIRTimer(我最喜欢的课程之一)会跟踪时间。这概述如下:

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]];
}

在上面的代码中,NSTimer可能不准确,但精确的时间由NIRTimer保留,0.01每隔NSThread秒向用户显示一次。

如果您确实需要在特定时间运行特定代码,我找到的最佳解决方案是使用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]; }

timerRunning

在这种情况下,请将NO设置为[self performSelectorOnMainThread:@selector(doSomething) withObject:nil waitUntilDone:NO]以停止计时器。如果您尝试做的事情不是线程安全的话,您可能还想使用[self doSomething]代替{{1}}。

有关精彩线程世界的更多信息,请查看Threading Programming Guide

答案 1 :(得分:0)

我假设您从当前时间开始,然后每次NSTimer关闭时,您将原始时间添加一分钟然后显示。您应该对计时器执行的操作是:每次计时器关闭时,重新读取当前时间并显示该计时器。

答案 2 :(得分:0)

您应该将计时器设置为每秒触发而不是每分钟。但是不要每秒更新您的标签。只需比较当前时间的新分钟数是否与上次更新标签时的分钟数不同,如果是,则更新标签。

答案 3 :(得分:0)

好吧,我刚刚编写了自己的小NSTimer包装器,名为MinuteTimer来解决问题。我已将其上传到pastebin。这个想法是在计时器发生得太快时重置计时器,以免日期冒险显示错误。

由于这是管道编程,我仍然渴望听到其他答案!