在开发中我们经常用到定时器,iOS为我们提供了多种定时器,包括NSTimer、CADisplayLink、GCD。
iOS中最基本的定时器,其通过RunLoop来实现,一般情况下较为准确,但当前循环耗时操作较多时,会出现延迟问题。同时,也受所加入的RunLoop的RunLoopMode影响,如果因为创建的NSTimer
默认是被加入到了 defaultMode
,比如在一个视图里面滑动tableview,当 Runloop
的Mode
变化时,当前的 NSTimer
就不会工作,故我们使用NSTimer需要用NSRunLoopCommonModes。
创建
NSTimer *timer1 = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(timerMethod1) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
释放
定时器的释放一定要先将其终止,而后才能销毁对象。
invalidate 、timer置为nil 防止循环引用。
GCD定时器是dispatch_source_t
类型的变量,其可以实现更加精准的定时效果。
dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue1);
self.timer = timer;
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"%@",[NSThread currentThread]);
});
dispatch_resume(timer);
CADisplayLink是基于屏幕刷新的周期,所以其一般很准时,每秒刷新60次。其本质也是通过RunLoop,所以不难看出,当RunLoop选择其他模式或被耗时操作过多时,仍旧会造成延迟。
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(test)];
[link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
使用CADisplayLink自己的addToRunLoop:forMode:方法,同时,由于其是基于屏幕刷新的,所以也度量单位是每帧,其提供了根据屏幕刷新来设置间隔的frameInterval
属性,其决定于屏幕刷新多少帧时调用一次该方法,默认为1,即1/60秒调用一次。
总结
iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。
NSTimer的精确度就显得低了点,比如NSTimer的触发时间到的时候,runloop如果在阻塞状态,触发时间就会推迟到下一个runloop周期。并且 NSTimer新增了tolerance属性,让用户可以设置可以容忍的触发的时间的延迟范围。
CADisplayLink使用场合相对专一,适合做UI的不停重绘,比如自定义动画引擎或者视频播放的渲染。NSTimer的使用范围要广泛的多,各种需要单次或者循环定时处理的任务都可以使用。在UI相关的动画或者显示内容使用 CADisplayLink比起用NSTimer的好处就是我们不需要在格外关心屏幕的刷新频率了,因为它本身就是跟屏幕刷新同步的。
NSTimer和CADisplayLink都需要加入runloop。
GCD更准时的原因,我们可以发现GCD定时器实际上是使用了dispatch源(dispatch source),dispatch源监听系统内核对象并处理。dispatch类似生产者消费者模式,通过监听系统内核对象,在生产者生产数据后自动通知相应的dispatch队列执行,后者充当消费者。通过系统级调用,更加精准。