一、NSTimer 关于定时器的时间误差问题。
1)创建一个循环定时器并且执行循环操作。
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.00 target:self selector:@selector(timerRun) userInfo:nil repeats:YES];
- (void)timerRun {
NSLog(@"timerTest");
}
打印结果如下:

这里的打印可以看出时间上有一定的误差,虽然这个误差已经很小了。但误差确实存在。。
如果在循环里面加上计算任务,
- (void)timerRun {
NSLog(@"timerTest");
int count = 0;
for (long i = 0; i < 1000000000; i++) {
count += 1;
}
再来看看打印的结果,

现在误差就很明显了。
2)原因分析:
定时器被添加在主线程中,由于定时器在一个RunLoop中被检测一次,所以如果在这一次的RunLoop中做了耗时的操作,当前RunLoop持续的时间超过了定时器的间隔时间,那么下一次定时就被延后了。
RunLoop 有三个模式,NSDefaultRunLoopMode; 默认模式
UITrackingRunLoopMode; UI滚动模式
NSRunLoopCommonModes; 上面两种模式的共同模式
NSTimer默认是添加在RunLoop的 NSDefaultRunLoopMode 模式中。
所以要将 NSTimer 添加在 NSRunLoopCommonModes 模式中;
二、GCD timer
//获得队列
dispatch_queue_t dispatchQueue = dispatch_get_global_queue(0, 0);
//创建一个gcd定时器 (dispatch_source_t timer:dispatch_source_t本质还是个OC对象,是个局部变量,需要强引用,不然下面使用无效)
self.gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatchQueue);
//OC对象%@可以打印
NSLog(@"%@", self.gcdTimer);
//设置定时器的各种属性(几时开始,每隔多长时间执行一次)
//GCD时间参数,一般是纳秒 NSEC_PER_SEC 纳秒单位等于一秒;DISPATCH_TIME_NOW当前时间
dispatch_time_t start = DISPATCH_TIME_NOW;
uint64_t interval = (uint64_t)(2.0 *NSEC_PER_SEC);
dispatch_source_set_timer(self.gcdTimer, start, interval, 0);
dispatch_source_set_event_handler(self.gcdTimer, ^{
[self timerRun];
});
//启动定时器
dispatch_resume(self.gcdTimer);
- (void)timerRun {
NSLog(@"timerTest");
int count = 0;
for (long i = 0; i < 1000000000; i++) {
count += 1;
}
}
GCD Timer 同样也会有一样的误差。

三、解决办法
1)将NStimer 添加在子线程中执行。然后在需要UI操作的地方放置在主线程里面。
dispatch_queue_t queue = dispatch_queue_create("const_char_dispatch_timer", DISPATCH_QUEUE_CONCURRENT);
__weak typeof(self)ws = self;
dispatch_async(queue, ^{
__strong typeof(ws)ss = ws;
ss.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerRun) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:ss.timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];
});
- (void)timerRun {
NSLog(@"timerTest");
dispatch_queue_t queue = dispatch_queue_create("const_char_dispatch_timer", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
int count = 0;
for (long i = 0; i < 1000000000; i++) {
count += 1;
}
});
}
网友评论