NSTimer的准确性

use*_*343 6 objective-c nstimer

我正在尝试使用NSTimer创建一个Stop-watch样式计时器,每0.1秒递增一次,但有时它似乎运行得太快了.

这就是我做到的方式:

Timer =[NSTimer scheduledTimerWithTimeInterval: 0.1 target:self selector:@selector(updateTimeLabel) userInfo:nil repeats: YES];
Run Code Online (Sandbox Code Playgroud)

然后:

-(void)updateTimeLabel
{
    maxTime=maxTime+0.1;
    timerLabel.text =[NSString stringWithFormat:@"%.1f Seconds",maxTime];
}
Run Code Online (Sandbox Code Playgroud)

这将在Label中显示计时器的值,稍后我可以将maxTime用作Timer停止的时间...

问题是它运行非常不准确.

有没有一种方法可以确保NSTimer严格每0.1秒严格发射一次?我知道NSTimer不准确,我要求进行调整以使其准确.

谢谢

Gab*_*lla 9

根据NSTimer文档,它并不准确.

由于典型的运行循环管理各种输入源,因此定时器的时间间隔的有效分辨率被限制在50-100毫秒的量级.如果在长时间标注期间或在运行循环处于不监视定时器的模式下发生定时器的触发时间,则定时器在下次运行循环检查定时器之前不会触发.因此,计时器可能发射的实际时间可能是在计划的发射时间之后的重要时间段.

您可能希望使用dispatch_afterGCD中的功能,这是官方文档为此目的建议的(创建计时器).

如果要在指定的时间间隔后执行一次块,则可以使用dispatch_afterdispatch_after_f功能.


顺便说一句,我同意Caleb的回答.如果你不像现在这样积累错误,你可能会解决你的问题.如果您使用该-timeIntervalSince:方法存储开始日期并在每次迭代时重新计算时间,那么无论计时器精度如何,您都将获得准确的UI更新.

  • 如果你使用重复,NSTimer不是"不精确". (4认同)
  • 你需要它,以免累积错误.如果在每次"迭代"时重新计算时间,则不会累积错误.否则,你总结了"NSTimer"的每一个不精确的结果,结果是一个非常不精确的度量. (2认同)

Cal*_*leb 5

maxTime=maxTime+0.1;
Run Code Online (Sandbox Code Playgroud)

这是错误的做法。您不想使用计时器来累积经过的时间,因为您会随之累积错误。使用计时器定期触发一个方法,该方法使用 计算经过的时间NSDate,然后更新显示。因此,更改您的代码来执行某些操作:

maxTime = [[NSDate date] timeIntervalSince:startDate];
Run Code Online (Sandbox Code Playgroud)

  • 我明白,但计时器可能会以某种方式关闭 50 毫秒。对于很多事情来说,这并不是一个大错误,但它会很快增加。没有人关心您是否“精确”每 0.1 秒更新一次显示,但是当您“确实”更新显示时,它应该尽可能准确。您可以通过基于当前时间和固定开始时间之间的差异进行计算来实现这一点,从而避免误差累积。 (4认同)

nie*_*bot 5

这是一个你可以用来做你想要的课程:

@interface StopWatch()
@property ( nonatomic, strong ) NSTimer * displayTimer ;
@property ( nonatomic ) CFAbsoluteTime startTime ;
@end

@implementation StopWatch

-(void)dealloc
{
    [ self.displayTimer invalidate ] ;
}

-(void)startTimer
{
    self.startTime = CFAbsoluteTimeGetCurrent() ;
    self.displayTimer = [ NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector( timerFired: ) userInfo:nil repeats:YES ] ;
}

-(void)stopTimer
{
    [ self.displayTimer invalidate ] ;
    self.displayTimer = nil ;

    CFAbsoluteTime elapsedTime = CFAbsoluteTimeGetCurrent() - self.startTime ;
    [ self updateDisplay:elapsedTime ] ;
}

-(void)timerFired:(NSTimer*)timer
{
    CFAbsoluteTime elapsedTime = CFAbsoluteTimeGetCurrent() - self.startTime ;
    [ self updateDisplay:elapsedTime ] ;
}

-(void)updateDisplay:(CFAbsoluteTime)elapsedTime
{
    // update your label here
}

@end
Run Code Online (Sandbox Code Playgroud)

关键点是:

  1. 通过在秒表启动变量时保存系统时间来计时.
  2. 当秒表停止时,通过从当前时间减去秒表开始时间来计算经过时间
  3. 使用计时器更新您的显示器.如果您的计时器是否准确无关紧要.如果您尝试保证显示更新至少每0.1秒,您可以尝试将定时器间隔设置为最小更新时间的1/2(0.05秒).