# 常用的延时调用

* iOS中的几种常用定时器,是否严格按照设定的时间间隔按时执行，支持的最小时间间隔

## NSTimer

* 可以精确到50-100毫秒. 能满足对间隔要求不严格、对精确度不敏感的需求
  * NSTimer理论上最小精度为 0.1 毫秒。不过由于受 Runloop 的影响，会有 50 \~ 100 毫秒的误差，所以，实际精度可以认为是 0.1 秒。
  * 不可靠，NSTimer不是绝对准确的,而且中间耗时或阻塞错过下一个点,那么下一个点就pass过去了.timer其所在的RunLoop会定时检测是否可以触发NSTimer的事件.但由于iOS有多个RunLoop的运行模式，如果被切到另一个runloop, NSTimer就不会被触发.每个RunLoop的循环间隔也无法保证,当某个任务耗时比较久,RunLoop的下一个消息处理就只能顺延,导致NSTimer的时间已经到达,但Runloop却无法及时触发 NSTimer,导致该时间点的回调被错过。
* 创建方法:

```
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;

- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(id)ui repeats:(BOOL)rep;
```

* 除了参数区别之外.这五种初始化方法的不同之处在于：
  * timerWith这两个类方法以及init方法,只是创建了定时器,必须手动加入到当前runloop中去.如果不手动加入主循环池中，将不会循环执行。并且如果不手动调用fire，则定时器不会启动。
  * scheduled这两个类方法是创建一个定时器，并加入到当前运行循环中.不需要手动调用fire，会自动执行。
* 使用 \[timer fire];可以触发定时器.
  * fire并不是启动一个定时器，只是提前触发而已.
  * 在重复执行(repeat = YES)的定时器中调用此方法后立即触发该定时器，但不会中断其之前的执行计划；
  * 在不重复执行(repeat = NO)的定时器中调用此方法，立即触发后，就会使这个定时器失效。
* 销毁定时器,使用 \[timer invalidate]; 这是唯一将定时器从循环池中移除的方法.

### NSObject的 performSelector

* NSObject对NSTimer封装后提供的一个比较简单的延时方法.当调用NSObject的performSelecter:afterDelay:后,实际上其内部会创建一个Timer并添加到当前线程的RunLoop中.所以这种方法的精确度和可靠性同timer.
* 使用方法

```
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes;
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
```

* cancle方法

```
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument;
```

## GCD

* 最小精度为纳秒,误差在50毫秒以内.
* 比较常用的 dispatch\_after方法并没有直接的cancel方法

```
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0/*延迟执行时间*/ * NSEC_PER_SEC));

dispatch_after(delayTime, dispatch_get_main_queue(), ^{

});
```

* 更加精确的定时器 消耗性能 &#x20;

```
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); //创建一个GCD的定时器 dispatch_source_t本质上是OC类，

dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW,3.0 *NSEC_PER_SEC); //设置开始时间
  uint64_t delayTime = 1.0 * NSEC_PER_SEC; //设置定时器工作的间隔时间

dispatch_source_set_timer(timer, start, delayTime, 0 * NSEC_PER_SEC);
   /*
    第一个参数：要给哪个定时器设置
    第二个参数：定时器的开始时间DISPATCH_TIME_NOW表示从当前开始
    第三个参数：定时器调用方法的间隔时间
    第四个参数：定时器的精准度，如果传0则表示采用最精准的方式计算，如果传大于0的数值，则表示该定时切换i可以接收该值范围内的误差，通常传0
    该参数的意义：可以适当的提高程序的性能
    注意点：GCD定时器中的时间以纳秒为单位
    */

//设置定时器开启后回调的方法
dispatch_source_set_event_handler(timer, ^{

    });
//执行定时器
dispatch_resume(timer);
```

* 取消方法

```
dispatch_source_cancel(_recordTimer);
```

### # 这些是比较常用的,一般使用场景下的也足够了,如果需要更精确的,系统有给提供误差更小的高精度定时器

*   详见

    [https://developer.apple.com/library/content/technotes/tn2169/\_index.html](https://developer.apple.com/library/content/technotes/tn2169/_index.html)
