NSTimer这个大家用的比较多,
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
// 取消的时候
[self.timer invalidate];
self.timer = nil;
在日常开发中,如果我们需要对定时器的精度要求很高的话,可以考虑dispatch_source_t
去实现 。GCD的timer代码量有点大
dispatch_queue_t queue = dispatch_queue_create("hhh", DISPATCH_QUEUE_SERIAL);
// timer要做成属性或者成员变量
self.gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(self.gcdTimer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0.5 * NSEC_PER_SEC);
dispatch_source_set_event_handler(self.gcdTimer, ^{
NSLog(@"gcd的timer fire");
});
dispatch_resume(self.gcdTimer);
// 演示如何取消定时器
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 取消定时器
dispatch_cancel(self.gcdTimer);
});
创建GCD的timer
dispatch_source_create(dispatch_source_type_t type, uintptr_t handle,
unsigned long mask,dispatch_queue_t _Nullable queue)
第一个参数:dispatch_source_type_t type为设置GCD源方法的类型。
/*
当同一时间,一个事件的的触发频率很高,那么Dispatch Source会将这些响应以ADD的方式进行累积,然后等系统空闲时最终处理,如果触发频率比较零散,那么Dispatch Source会将这些事件分别响应。
*/
DISPATCH_SOURCE_TYPE_DATA_ADD 自定义的事件,变量增加
DISPATCH_SOURCE_TYPE_DATA_OR 自定义的事件,变量OR
DISPATCH_SOURCE_TYPE_DATA_REPLACE 自定义的事件,变量Replace
DISPATCH_SOURCE_TYPE_MACH_SEND MACH端口发送
DISPATCH_SOURCE_TYPE_MACH_RECV MACH端口接收
DISPATCH_SOURCE_TYPE_MEMORYPRESSURE 内存报警
DISPATCH_SOURCE_TYPE_PROC 进程监听,如进程的退出、创建一个或更多的子线程、进程收到UNIX信号
DISPATCH_SOURCE_TYPE_READ IO操作,如对文件的操作、socket操作的读响应
DISPATCH_SOURCE_TYPE_SIGNAL 接收到UNIX信号时响应
DISPATCH_SOURCE_TYPE_TIMER 定时器
DISPATCH_SOURCE_TYPE_VNODE 文件状态监听,文件被删除、移动、重命名
DISPATCH_SOURCE_TYPE_WRITE IO操作,如对文件的操作、socket操作的写响应
DISPATCH_MACH_SEND_DEAD
第二个参数:uintptr_t handle , Apple的API介绍说,暂时没有使用,传0即可。
第三个参数:unsigned long mask, Apple的API介绍说,使用DISPATCH_TIMER_STRICT,会引起电量消耗加剧,毕竟要求精确时间,所以一般传0即可,视业务情况而定。
第四个参数:dispatch_queue_t _Nullable queue 队列,将定时器事件处理的Block提交到哪个队列之上。可以传Null,默认为全局队列。
返回值: dispatch_source_t 就是描述GCDTimer的一个结构体,包括 在哪个线程 , 执行什么, 间隔多少 等信息,
设置timer属性
dispatch_source_set_timer(dispatch_source_t source,
dispatch_time_t start,
uint64_t interval,
uint64_t leeway);
第一个参数: 要设置哪个timer.
第二个参数:dispatch_time_t start
, 定时器开始时间,类型为 dispatch_time_t
,其API的abstract标明可参照dispatch_time()
和dispatch_walltime()
,同为设置时间,但是后者为“钟表”时间,相对比较准确,所以选择使用后者。dispatch_walltime(const struct timespec *_Nullable when, int64_t delta)
,参数when
可以为Null
,默认为获取当前时间,参数delta
为增量,即获取当前时间的基础上,增加X秒的时间为开始计时时间,此处传0,传
DISPATCH_TIME_NOW都想。
第三个参数:uint64_t interval
,定时器间隔时长,多长时间触发一次, 由业务需求而定。
第四个参数:uint64_t leeway
, 允许误差,此处传0即可。
设置timer对应的事件
dispatch_source_set_event_handler(dispatch_source_t source,
dispatch_block_t _Nullable handler)
第二个参数:dispatch_block_t _Nullable handler,定时器执行的动作,需要处理的业务逻辑Block。
启动timer
dispatch_resume(_timer)
定时器创建完成并不会运行,需要主动去触发,也就是调用上述方法。
调度源提供了源事件的处理回调,同时也提供了取消源事件处理的回调,使用非常方便。
取消timer
dispatch_source_set_cancel_handler(dispatch_source_t source,
dispatch_block_t _Nullable handler)
上面在使用GCD的timer的时候使用了dispatch_source_XXX方法, 那么Dispatch Source是什么呢?
GCD中除了主要的Dispatch Queue外,还有较次要的Dispatch Source
。它是BSD系内核惯有功能kqueue
的包装。kqueue
是在XUN内核中发生各种事件时,在应用程序编程方执行处理的技术。其CPU负荷非常小,尽量不占用资源。kqueue
可以说是应用程序处理XUN内核中发生的各种事件的方法中最优秀的一种。
Dispatch source
替代了异步回调函数,来处理系统相关的事件。
当你配置一个dispatch source时,你指定要监测的事件、dispatch queue、以及处理事件的代码(block或函数)。
当事件发生时,dispatch source
会提交你的block或函数到指定的queue去执行和手工提交到queue的任务不同,dispatch source
为应用提供连续的事件源。除非你显式地取消,dispatch source会一直保留与dispatch queue的关联。
只要相应的事件发生,就会提交关联的代码到dispatch queue去执行。
为了防止事件积压到dispatch queue,dispatch source实现了事件合并机制。 如果新事件在上一个事件处理器出列并执行之前到达,dispatch source会将新旧事件的数据合并。 根据事件类型的不同,合并操作可能会替换旧事件,或者更新旧事件的信息。
dispatch_source_create
创建的dispatch_source_t
默认是出于挂起状态的。Dispatch sources are created in an inactive state.此时dispatch source会接收事件,但是不会进行处理。如要使用需用dispatch_resume
来开始执行。
也可以使用dispatch_suspend(暂停)
和dispatch_resume(恢复)
来控制dispatch_source_t
的事件执行
最后,主要比对下2者的区别 :
- NSTimer 需要一个运行的Runloop 来处理其定时任务, MainThread是一直启动并运行的,默认加入了NSDefaultRunLoopMode
- 在自定的线程如果使用NSTIme必须手动开启并运行子线程的Runloop,[[NSRunLoop currentRunLoop] run]; 并且销毁子线程的timer也必须在子线程中,也就是说创建和 invalidate必须放在相同的线程中进行
- NSTimer 必须调用 invalidate 来停止其定时任务,并且NSTimer 对其Target是强引用,要注意Target 与 - NSTimer间造成的循环引用造成的内存泄漏(可以封装一个类别来解决此问题)
- NSTimer 的fire函数,相当于立即调用一个绑定的事件,对原来的时间周期没有影响
- GCDTimer 是基于GCD实现的,使用的时候只要我们把任务提交给相应队列就好
- GCDTimer 在使用时要注意 dispatch_resume(obj) dispatch_suspend(obj) -dispatch_source_cancel(obj)API 的使用
- GCDTimer 在对文件资源定期进行读写操作时很方便,其他与NSTimer使用场景差不多
NSTimer不准时的原因:
1:RunLoop循环处理的时间,可能某个时刻runloop需要处理很多任务,会导致NSTimer的精度降低,
2:受RunLoop模式的影响,如果NSTimer没有加入到NSRunLoopCommonModes的话,就会受到UITrackingRunLoopMode和NSDefaultRunLoopMode的切换影响
gcd的timer与NSTimer是不同的
1:都是源,而NSTimer是RunLoop的源 ;gcd的timer是dispatch的源,,dispatch_source_t精度很高,系统自动触发。
2:gcd的timer不需要加入mode,那么就不会受到切换模式的影响了