【问题标题】:NSTimer inaccurate?NSTimer 不准确?
【发布时间】:2011-10-06 17:33:20
【问题描述】:

我正在使用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 秒的时间间隔,而是稍短一些。

【问题讨论】:

  • 很难说不知道如何计算间隔并启动计时器
  • 你是如何计算nextFullMinuteDate的?
  • @PeterHosey:看here
  • @PeterHosey:另外,请查看我在下面的答案中编写并发布的MinuteTimer 课程。这应该可以让您快速创建 SSCCE。

标签: cocoa nstimer


【解决方案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]。

如需更多关于线程精彩世界的信息,请查看Threading Programming Guide。

【讨论】:

    【解决方案2】:

    我假设您从当前时间开始,然后每次您的 NSTimer 关闭时,您都会在原始时间上增加一分钟,然后显示它。你应该对你的计时器做的是:每次计时器关闭时,重新读取当前时间并显示它。

    【讨论】:

    • 您还应该更频繁地触发计时器,而不是每分钟一次。
    • 因为计时器似乎启动得太早了。比如 2:23:59.999 而不是 2:24:00。至少这是我认为正在发生的事情。
    • 对,所以有时您的时间会延迟 1 分钟,最长持续时间为 1 分钟。尝试每秒或每 10 秒(或任何您确定的最小可接受错误)触发计时器。
    【解决方案3】:

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

    【讨论】:

    • 抱歉,这听起来效率很低。
    【解决方案4】:

    好吧,我刚刚编写了自己的小NSTimer 包装器,称为MinuteTimer 来“解决”问题。我已经上传到pastebin。我们的想法是在计时器启动太快以至于日期显示不正确时重置计时器。

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

    【讨论】:

      猜你喜欢
      • 2023-04-02
      • 1970-01-01
      • 2016-07-21
      • 2023-03-16
      • 2013-09-21
      • 2021-07-28
      • 2013-09-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多