NSTimer的最短有效时间间隔是多少?

时间:2017-06-25 09:19:09

标签: ios objective-c nstimer

我想使用NSTimer来增加标签上显示的数字。

这是我的代码:

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

self.numberLabel = [[UILabel alloc]initWithFrame:CGRectMake(90, 90, 90, 30)];
[self.view addSubview:self.numberLabel];


self.timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(refreshText) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop]addTimer:self.timer forMode:NSRunLoopCommonModes];
}

- (void)refreshText{

NSDate *beginDate = [NSDate date];

static NSInteger a = 0;
a ++;

self.numberLabel.text = [NSString stringWithFormat:@"%ld",a];

if (a == 1000) {

    NSDate *endDate = [NSDate date];

    NSTimeInterval durationTime = [endDate timeIntervalSinceDate:beginDate];
    NSTimeInterval intervalTime = self.timer.timeInterval;

    NSLog(@"durationTime = %f",durationTime);
    NSLog(@"intervalTime = %f",intervalTime);

    [self.timer invalidate];
    self.timer = nil;
} 
}

并且控制台显示: enter image description here

然后我将计时器的timeInterval从0.01更改为0.001,控制台显示: enter image description here

让我感到困惑的是,当timeInterval为0.001时,为什么durationTime不是0.0000056。还有更多,NSTimer的最小值是否可以设置timeInterval?

2 个答案:

答案 0 :(得分:3)

NSTimer的时间段是NSTimeInterval类型的值,而这提供的sub-millisecond precision对您没有帮助。从NSTimer documentation开始:

  

计时器与运行循环一起使用。运行循环保持对其计时器的强引用,因此在将计时器添加到运行循环后,您不必维护自己对计时器的强引用。

     

要有效地使用计时器,您应该了解运行循环的运行方式。有关详细信息,请参阅“线程编程指南”。

     

计时器不是实时机制。如果计时器的触发时间在长时间运行循环标注期间发生,或者运行循环处于不监视计时器的模式,则计时器在下次运行循环检查计时器之前不会触发。因此,计时器触发的实际时间可能明显晚。另见Timer Tolerance。

因此NSTimer的最小时间间隔与运行循环迭代的长度相关联。虽然内部优化(如果存在)可以在设置时立即触发计时器,如果间隔非常小,一般来说,您将获得的最短时间取决于运行循环迭代的剩余执行时间,其中计时器是set,这对于通用编程几乎是不确定的。

如果真的需要高分辨率计时器(请参阅@ bbum对您的问题的评论),那么您需要研究该主题 - 只需搜索类似“高分辨率计时macOS”的内容起点。

HTH

答案 1 :(得分:2)

有一个更好的方法来解决您的问题。使用CADisplayLink代替NSTimer。 CADisplayLink允许您在每次屏幕刷新时尽快更新UI。没有什么比屏幕刷新频率更频繁地更新UI了,因此NSTimer并不是快速更新UI的最佳工具。

func beginUpdates() {
  self.displayLink = CADisplayLink(target: self, selector: #selector(tick))    
  displaylink.add(to: .current, forMode: .defaultRunLoopMode)
}

func tick() {
  // update label
}

func endUpdates(){
  self.displayLink.invalidate()
  self.displayLink = nil
}