# 09.NSTimer与GCD

## 一: 常用延时调用的方式

* NSObject的performSelector...
  * 实际是在当前线程创建了一个Timer去执行任务
* NSTimer
  * +timerWithTimeInterval
  * +scheduledTimerWithTimeInterval
* GCDTimer
  * dispatch\_after

## 二: 非GCDTimer使用注意

* 必须有一个活跃的`RunLoop`

> `performSelector` 和`scheduledTimerWithTimeInterval` 都是基于RunLoop的. 主线程没关系,因为不会停止 如果在子线程的话需要手动开启RunLoop否则调用将会是无效的

* NSTimer的创建于撤销必须在同一个线程操作
* `performSelector`创建取消也必须在同一个线程操作
* 内存泄露问题
  * Timer的target会被Timer强引用
  * Timer被RunLoop强引用
  * 所以想在`dealloc`里去`invalidate` Timer是不可能的
  * 计时完成后手动去`invalidate`
    * RunLoop解除对Timer的引用
    * Timer解除对Target的引用

## 三: GCDTimer

* 系统会自动处理线程逻辑
* 无须关心RunLoop问题
  * block的循环引用还是要注意下
    * 缺点
    * 没有repeats选项
    * 直接使用代码比较多,最好封装一下

![](https://upload-images.jianshu.io/upload_images/595974-438c0a9cb7a27160.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1000)

## 四: 时间准确性

### NSTimer-较低

> CFRunloopTimerRef 底层基于 `mk_timer` 实现 基于RunLoop运行 使用之前必须注册到RunLoop,但RunLoop为节省资源并不会在非常准确的时机调用定时器,执行任务时,可能错过一个时间点只能等下个时间点再执行 NSTimer有个`tolerance`属性可以用于设置宽容度,可提高精准度

### GCDTimer-准确

### CADisplayLink-较准确

> CADisplayLink是一个执行频率（fps）和屏幕刷新相同（可以修改preferredFramesPerSecond改变刷新频率）的定时器，它也需要加入到RunLoop才能执行。与NSTimer类似，CADisplayLink同样是基于CFRunloopTimerRef实现，底层使用mk\_timer（可以比较加入到RunLoop前后RunLoop中timer的变化）。和NSTimer相比它精度更高（尽管NSTimer也可以修改精度），不过和NStimer类似的是如果遇到大任务它仍然存在丢帧现象。通常情况下CADisaplayLink用于构建帧动画，看起来相对更加流畅，而NSTimer则有更广泛的用处。

## 参考

> [https://www.jianshu.com/p/0c050af6c5ee](https://www.jianshu.com/p/0c050af6c5ee) [https://www.cnblogs.com/kenshincui/p/6823841.html](https://www.cnblogs.com/kenshincui/p/6823841.html)
