iOS【NSTimer到底准不准?】
2018.2.1 最近面试 被 问道 NSTimer 到底准不准的问题 当时有点蒙 因为没爬过这个坑 所以没法打出来 特此记录一下
当时 就知道 人家 问了 那么就说明 NSTimer 一定是不准的 但是 问题点就是 在这个 准不准的标准是什么呢 ?
定时器 很简单 回来后做了个测试 自己写了个 定时器
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (!_timer) {
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(logInfo) userInfo:nil repeats:YES];
}
}
- (void)logInfo {
NSLog(@"timer test");
}
结果是
2018-02-01 09:12:32.566622+0800 QTimer[20276:7878806] timer test
2018-02-01 09:12:33.566811+0800 QTimer[20276:7878806] timer test
2018-02-01 09:12:34.566510+0800 QTimer[20276:7878806] timer test
2018-02-01 09:12:35.567532+0800 QTimer[20276:7878806] timer test
2018-02-01 09:12:36.567613+0800 QTimer[20276:7878806] timer test
2018-02-01 09:12:37.566615+0800 QTimer[20276:7878806] timer test
2018-02-01 09:12:38.567415+0800 QTimer[20276:7878806] timer test
2018-02-01 09:12:39.567650+0800 QTimer[20276:7878806] timer test
2018-02-01 09:12:40.566592+0800 QTimer[20276:7878806] timer test
可以看出来 定时器 并不是准确的 1S的 误差 在 1毫秒以内 应该是很准的了
但是 确实 是不准的 我们继续 给 定时器 的方法里面 继续 添加更多的耗时代码;
- (void)logInfo {
int count = 0;
for (int i = 0; i < 1000000000; i++) {
count += i;
}
NSLog(@"timer test");
}
结果
2018-02-01 09:40:38.194879+0800 QTimer[9749:3330951] timer test
2018-02-01 09:40:44.188463+0800 QTimer[9749:3330951] timer test
2018-02-01 09:40:50.172012+0800 QTimer[9749:3330951] timer test
2018-02-01 09:40:56.172139+0800 QTimer[9749:3330951] timer test
2018-02-01 09:41:02.179022+0800 QTimer[9749:3330951] timer test
2018-02-01 09:41:08.170254+0800 QTimer[9749:3330951] timer test
2018-02-01 09:41:14.169011+0800 QTimer[9749:3330951] timer test
可以清晰的看出来 时间已经不准的 准确的来说 那么原因是什么呢 经过 查找资料得知
定时器被添加在主线程中,由于定时器在一个RunLoop中被检测一次,所以如果在这一次的RunLoop中做了耗时的操作,当前RunLoop持续的时间超过了定时器的间隔时间,那么下一次定时就被延后了。
意思就是说 你这个 定时器的方法里面 相当于 一个runloop 只有当 方法 里面代码 走完了 定时器 才会 继续 起作用 就是说 当 定时器 方法 里面 的耗时操作 超过了 定时器的时间间隔 那么 就会 导致 定时方法里面的代码 执行之后 才会继续定时器 再间隔一s 之后执行这个方法 (这个情况 会出现的 前提是 定时器里面的方法 的耗时操作 超过了 定时的 时间间隔);
接下来就是 解决方法了 既然 执行方法 会 阻碍NSTtimer 那么我们就把他们分开
解决方法:
1、在子线程中创建timer,在主线程进行定时任务的操作
2、在子线程中创建timer,在子线程中进行定时任务的操作,需要UI操作时切换回主线程进行操作
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread)object:nil];2[thread start];
- (void)newThread2{
@autoreleasepool
{
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(addTime) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] run];
}
}
在子线程中将NSTimer以默认方式加到该线程的runloop中,启动子线程。(注意 timer 的runloop 模式的形象 因为是在子线程里面 所以 runloop的model 没考虑 如果特殊情况 考虑runloop的model 就不采用 默认的方式创建timer 手动添加 到runloop 里面 去 采用commodmodel )
或者干脆 直接用多线程GCD 定时器 也是 多线程方式 回归主线程 刷新UI
网友评论