定时器

作者: 学呀学呀总得学会了吧 | 来源:发表于2018-12-07 18:24 被阅读12次

    【NSTimer】

    1.NSTimer是最为常见的一种定时方式,根据文档可以知道它的使用方式也比较简单

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

    创建一个计时器,参数1:时间间隔(秒),参数2:表示发送的对象(一般填self),参数3:要执行的方法,参数4:传递信息(可以以字典的形式,将信息传递给要执行的方法),参数5:是否重复执行,如果NO,timer执行一次后便失效。

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

    创建一个计时器,参数1:时间间隔(秒),参数2:表示发送的对象(一般填self),参数3:要执行的方法,参数4:传递信息(可以以字典的形式,将信息传递给要执行的方法),参数5:是否重复执行,如果NO,timer执行一次后便失效。

    两者的区别:scheduledTimerWithTimeInterval方法创建完timer之后,会自动以NSDefaultRunLoopModel模式加入运行循环。而timerWithTimeInterval方法创建的timer,不会自动加入运行循环,需要我们手动指定模式,并手动加入运行循环。

    看下边的图更容易理解:

          Cell上添加了一个定时器,然后没1秒更新一次时间,并在Cell的timeLabel上显示,运行效果如下所示。从该运行效果中我们不难发现,当我们滑动TableView时,该定时器就停止了工作。具体原因就是当前线程的RunLoop在TableView滑动时将DefaultMode切换到了TrackingRunLoopMode。因为Timer默认是添加在RunLoop上的DefaultMode上的,当Mode切换后Timer就停止了运行。

    改进:

    注意:

    Nonrepeating Timer运行完自行无效,以防再次触发,但Repeating Timer需要手动调用

    [self.timer invalidate];

    self.timer =nil;


    NSTimer非实时性的原因:

    a. 一个是可能因为当前RunLoop运行的Mode不监听导致未能触发

    b. 一个可能当前RunLoop做了耗时的工作,使得持续时间超过了一个或若干个NSTimer的触发时间,NSTimer不会进行补偿操作,只是此次循环检查定时器时触发NSTimer,但是不影响后面其它的触发时间,因为NSTimer根据一开始计划的时间来触发,并不根据每次被触发的实际时间点来计算下一次的触发时间

    c.fire方法立即触发时间,对于Nonrepeating Timer运行完自行无效,但是跟 b 点一样,不影响Repeating Timer的预定周期性触发

    【CADisplayLink】

    CADisplayLink是基于屏幕刷新周期的定时器,一般 60 frames/s,同样也是基于RunLoop,因此也会碰到因为运行在非定时触发的Mode或者工作耗时导致的延迟(这点跟NSTimer一样),这里需要注意的是回调工作若是当前线程大量计算,也会导致下一次的延迟,掉帧卡顿发生

    CADisplayLink因为同步屏幕刷新频率,屏幕刷新后立即回调,因此很适合跟 UI 相关的定时绘制操作,像进度条、FPS等等,这样就无须进行多余运算

    同样 CADisplayLink 会对 target 持有,所以记得进行释放,以免造成内存泄露

    【GCD】

    Grand Central Dispatch简称GCD,一套低层API,提供了简单易用并无需直接操作线程居于队列的并发编程,详情查阅

    GCD功能非常强大,今天只涉及Dispatch Sources中的定时器

    Dispatch Sources替换了处理系统相关事件的异步回调函数。配置一个dispatch source,需要指定要监测的事件(DISPATCH_SOURCE_TYPE_TIMER等)、dispatch queue、以及处理事件的代码(block或C函数)。当事件发生时,dispatch source会提交对应的block或C函数到指定的dispatch queue执行

    Dispatch Sources监听系统内核对象并处理,通过系统级调用,会比NSTimer更精准一些


    【高精度定时】

    以上的几种定时都会受限于苹果为了保护电池和提高性能采用的策略而导致有延迟,像NSTimer会有50-100毫秒的误差,若的确需要使用更高精度的定时器(误差小于0.5毫秒),一般在多媒体操作方面有所需要,苹果官方同样也提供了方法,阅读高精度定时文档

    高精度定时用到的比较少,一般视频或者音频相关数据流操作中需要

    其实现原理就是使定时器的线程优先于系统上的其他线程,在无多线程冲突的情况下,这定时器的请求会被优先处理,所以不要创建大量的实时线程,一旦某个线程都要被优先处理,结果就是实时线程都失败了

    所以现在实现高精度定时有两种方法:

    1.使用Mach Thread API把定时器所在的线程,移到高优先级的线程调度类即the real time scheduling class

    2.使用更精确的计时API mach_wait_until(),如下代码使用mach_wait_until()等待10秒



    【总结】

    以上的所有定时器,虽然提供方法各不相同,但它们的内核代码是相同的

    NSTimer平时用的比较多,一般也足够,需要注意的就是加入的RunLoop的Mode或者若是子线程,需要手动启动并运行RunLoop;同时注意使用invalidate手动停止定时,否则引起内存泄漏;

    需与显示更新同步的定时,建议CADisplayLink,可以省去多余计算

    GCD定时精度较NSTimer高,使用时只需将任务提交给相应队列即可,对文件资源等定期读写操作很方便,使用时需要⚠️dispatch_resume与dispatch_suspend配套,同时要给dispatch source设置新值或者置nil,需先dispatch_source_cancel(timer)后

    高精度定时,使用较少,一般多媒体视频流/音频流处理相关需要

    实际上,苹果也不推荐使用太高精度的定时器,对于NSTimer,精度在50-100ms都是正常的,如果我们需要足够高精度地进行计时,比如统计APP启动时间、一段任务代码的运行时间等等,NSTimer不是一个好的选择,mach_absolute_time()或者可以帮到你,苹果开发工具也带有更专业的API或者插件提供给开发者。


    此文为转发用于记录总结 ,感谢大神们的总结分享。

    链接:https://www.jianshu.com/p/d5845842b7d3

    https://www.cnblogs.com/ludashi/p/7349535.html

    https://www.jianshu.com/p/ed00615c3014

    相关文章

      网友评论

        本文标题:定时器

        本文链接:https://www.haomeiwen.com/subject/onhxhqtx.html