前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈 NSTimer 是否精确?

浅谈 NSTimer 是否精确?

作者头像
s_在路上
发布2018-09-30 10:56:26
5840
发布2018-09-30 10:56:26
举报
文章被收录于专栏:iOS 开发杂谈

面试的时候遇到一个问题,问 NSTimer 用做定时器的时候是否精确?

分析

A timer that fires after a certain time interval has elapsed, sending a specified message to a target object. Timers work in conjunction with run loops. Run loops maintain strong references to their timers, so you don’t have to maintain your own strong reference to a timer after you have added it to a run loop.

NSTimer 的启动依赖 RunLoop,如果在主线程中做了耗时的操作,当前 RunLoop 持续的时间超过了定时器的间隔时间,那么下一次定时就被延后了。

代码语言:javascript
复制
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;

Creates a timer and schedules it on the current run loop in the default mode.

如果调用的是 scheduledTimerWith:target:selector:userInfo:repeats,创建并安排到 RunLoopdefault mode 中。

You must add the new timer to a run loop, using addTimer:forMode:

如果调用的是 timerWithTimeInterval:target:selector:userInfo:repeats 就需要手动 addRunLoopMode 中。

验证

主线程中不做耗时操作
代码语言:javascript
复制
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(countDown) userInfo:nil repeats:YES];

- (void)countDown {
    NSLog(@"timer test");
}

2018-09-15 13:29:19.042045+0800 NSTimer&CADisplayLink[60411:3510935] timer test
2018-09-15 13:29:20.041936+0800 NSTimer&CADisplayLink[60411:3510935] timer test
2018-09-15 13:29:21.041975+0800 NSTimer&CADisplayLink[60411:3510935] timer test
2018-09-15 13:29:22.041915+0800 NSTimer&CADisplayLink[60411:3510935] timer test
2018-09-15 13:29:23.042830+0800 NSTimer&CADisplayLink[60411:3510935] timer test
2018-09-15 13:29:24.041953+0800 NSTimer&CADisplayLink[60411:3510935] timer test
2018-09-15 13:29:25.041902+0800 NSTimer&CADisplayLink[60411:3510935] timer test
2018-09-15 13:29:26.042256+0800 NSTimer&CADisplayLink[60411:3510935] timer test
2018-09-15 13:29:27.042001+0800 NSTimer&CADisplayLink[60411:3510935] timer test
2018-09-15 13:29:28.042379+0800 NSTimer&CADisplayLink[60411:3510935] timer test
2018-09-15 13:29:29.042492+0800 NSTimer&CADisplayLink[60411:3510935] timer test

从结果中可以看出,如果在主线程中没有做耗时的操作,其计时偏差基本在1毫秒以内。

主线程中做耗时操作
代码语言:javascript
复制
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(countDown) userInfo:nil repeats:YES];

- (void)countDown {
    int count = 0;
    for (int i = 0; i < 1000000000; i++) {
        count += i;
    }
    NSLog(@"timer test");
}

2018-09-15 13:34:04.723817+0800 NSTimer&CADisplayLink[60575:3527468] timer test
2018-09-15 13:34:07.633158+0800 NSTimer&CADisplayLink[60575:3527468] timer test
2018-09-15 13:34:10.648023+0800 NSTimer&CADisplayLink[60575:3527468] timer test
2018-09-15 13:34:13.678671+0800 NSTimer&CADisplayLink[60575:3527468] timer test

从结果中可以看出,如果在主线程中做耗时的操作,其计时偏差已经去到了3秒之多,显然是有问题的。

解决方法

主线程启动定时器

在主线程中创建 timer,把耗时操作放在子线程执行,需要 UI 操作时切换回主线程进行操作。

代码语言:javascript
复制
NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(countDown) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

- (void)countDown {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        int count = 0;
        for (int i = 0; i < 1000000000; i++) {
            count += i;
        }
    });
    NSLog(@"timer test");
}

2018-09-15 13:55:07.515612+0800 NSTimer&CADisplayLink[60884:3544515] timer test
2018-09-15 13:55:08.515400+0800 NSTimer&CADisplayLink[60884:3544515] timer test
2018-09-15 13:55:09.515611+0800 NSTimer&CADisplayLink[60884:3544515] timer test
2018-09-15 13:55:10.515604+0800 NSTimer&CADisplayLink[60884:3544515] timer test
2018-09-15 13:55:11.515628+0800 NSTimer&CADisplayLink[60884:3544515] timer test
2018-09-15 13:55:12.515612+0800 NSTimer&CADisplayLink[60884:3544515] timer test
2018-09-15 13:55:13.514974+0800 NSTimer&CADisplayLink[60884:3544515] timer test

从结果中可以看出,在主线程中把耗时操作放在子线程执行,需要 UI 操作时切换回主线程进行操作,其计时偏差基本在1毫秒以内。

子线程启动定时器

在子线程中创建 timer,在子线程中进行定时任务的操作,需要 UI 操作时切换回主线程进行操作。

代码语言:javascript
复制
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(countDown) userInfo:nil repeats:YES];
});

运行结果中,没有看到任何打印日志。 在主线程默认启动了 RunLoop,可是子线程没有默认的 RunLoop,因此,我们在子线程启动定时器是不生效的。

代码语言:javascript
复制
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(countDown) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    [[NSRunLoop currentRunLoop] run];
});

- (void)countDown {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        int count = 0;
        for (int i = 0; i < 1000000000; i++) {
            count += i;
        }
    });
    
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"timer test");
    });
}

2018-09-15 13:58:18.471362+0800 NSTimer&CADisplayLink[60938:3548123] timer test
2018-09-15 13:58:19.471083+0800 NSTimer&CADisplayLink[60938:3548123] timer test
2018-09-15 13:58:20.471372+0800 NSTimer&CADisplayLink[60938:3548123] timer test
2018-09-15 13:58:21.471068+0800 NSTimer&CADisplayLink[60938:3548123] timer test
2018-09-15 13:58:22.471589+0800 NSTimer&CADisplayLink[60938:3548123] timer test
2018-09-15 13:58:23.471112+0800 NSTimer&CADisplayLink[60938:3548123] timer test
2018-09-15 13:58:24.470897+0800 NSTimer&CADisplayLink[60938:3548123] timer test
2018-09-15 13:58:25.470970+0800 NSTimer&CADisplayLink[60938:3548123] timer test
2018-09-15 13:58:26.471093+0800 NSTimer&CADisplayLink[60938:3548123] timer test
2018-09-15 13:58:27.470769+0800 NSTimer&CADisplayLink[60938:3548123] timer test
2018-09-15 13:58:28.469423+0800 NSTimer&CADisplayLink[60938:3548123] timer test
2018-09-15 13:58:29.471586+0800 NSTimer&CADisplayLink[60938:3548123] timer test

从结果中可以看出,在子线程中启动定时器,把耗时操作放在子线程执行,需要 UI 操作时切换回主线程进行操作,其计时偏差基本在1毫秒以内。

总结

NSRunLoop 的问题请查看这里 从结果看,NSTimer 在其使用场景下足够准了,其计时偏差基本在1毫秒以内也在容忍范围之内,如果想使用更精确的定时器,可以使用 CADisplayLink 或者 GCDTimer

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018.09.15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 分析
  • 验证
  • 解决方法
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档