前言
没有绝对准确的Timer,相对而言推荐CADisplayLink。
NSTimer
用法一:
scheduledTimerWithTimeInterval创建的timer:默认立即在当前线程runloop的NSDefaultRunLoopMode下运行,当前线程如果是主线程,如果切换到其他model(如NSEventTrackingRunLoopMode),timer不会被call,造成定时器不准确。此时需要将timer提交到NSRunLoopCommonModes模式。
// 创建定时器1
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(call) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
用法二
timerWithTimeInterval创建的timer:默认不添加到任何runloop中,需要将timer提交到NSRunLoopCommonModes模,并开启runloop。
// 创建定时器2
NSTimer *timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(call) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//如果是子线程
[[NSRunLoop currentRunLoop] run];
dispatch_source_t
使用:
//创建GCD timer资源, 第一个参数为源类型, 第二个参数是资源要加入的队列
_timer =dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0,0, queue);
//设置timer信息
dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), 1ull * NSEC_PER_SEC, 0);
//设置timer的触发事件
dispatch_source_set_event_handler(_timer, ^{
//do something
});
//激活timer对象
dispatch_resume(_timer);
特点:
需要持有dispatch_source_t变量。
优点:不受当前runloopMode的影响。
缺点:其计时效应仍不是百分之百准确的;另外,他的触发事件也有可能被阻塞,当GCD内部管理的所有线程都被占用时,其触发事件将被延迟。
CADisplayLink
特点:
CADisplayLink是一个和屏幕刷新率同步的定时器类。CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息,CADisplayLink类对应的selector就会被调用一次,所以可以使用CADisplayLink做一些和屏幕操作相关的操作。
缺点:
如果CPU操作饱和,影响了屏幕刷新,也会影响计时器的精准。
// 创建displayLink
displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(test)];
// 将创建的displaylink添加到runloop中,否则定时器不会执行
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];