ios NSTimer和GCDtimer对比

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,那么就不会受到切换模式的影响了

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值