常用的延时调用

  • 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(), ^{
});
  • 更加精确的定时器 消耗性能

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);

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