iOS Timer

作者: luckySmileBoy | 来源:发表于2018-03-07 16:25 被阅读68次

iOS开发中定时器经常会用到,iOS中常用的定时器有三种,分别是NSTime,CADisplayLink和GCD。

NSTimer

  • 方式1

// 创建定时器
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(test) userInfo:nil repeats:YES];

TimerInterval : 执行之前等待的时间。比如设置成1.0,就代表1秒后执行方法
target : 需要执行方法的对象。
selector : 需要执行的方法
repeats : 是否需要循环

// 停止定时器
[timer invalidate];
  • 注意 :

调用创建方法后,target对象的计数器会加1,直到执行完毕,自动减1。如果是循环执行的话,就必须手动关闭,否则可以不执行释放方法。

  • 方式2

// 创建定时器
NSTimer *timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(test) userInfo:nil repeats:YES];

// 将定时器添加到runloop中,否则定时器不会启动
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

// 停止定时器
[timer invalidate];

方式1和方式2在设置后都会在间隔设定的时间(本例中设置为2s)后执行test方法,如果需要立即执行可以使用下面的代码。

[time fire];

特性:

  • 存在延迟
    不管是一次性的还是周期性的timer的实际触发事件的时间,都会与所加入的RunLoop和RunLoop Mode有关,如果此RunLoop正在执行一个连续性的运算,timer就会被延时出发。重复性的timer遇到这种情况,如果延迟超过了一个周期,则会在延时结束后立刻执行,并按照之前指定的周期继续执行
  • 必须加入Runloop
    方式1会自动把timer加入MainRunloop的NSDefaultRunLoopMode中,而无需手动添加。但是在此种模式下,当滚动屏幕时runloop会进入另外一种模式,定时器会暂停,为了解决这种问题,可以像方式2那样把定时器添加到NSRunLoopCommonModes模式下。

CADisplayLink

// 创建displayLink
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(test:)];

// 将创建的displaylink添加到runloop中,否则定时器不会执行
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

// 停止定时器
[displayLink invalidate];
displayLink = nil;

当把CADisplayLink对象add到runloop中后,selector就能被周期性调用,类似于重复的NSTimer被启动了;执行invalidate操作时,CADisplayLink对象就会从runloop中移除,selector调用也随即停止,类似于NSTimer的invalidate方法,当然也可手动移除

[displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

特性

  • 屏幕刷新时调用
    CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息, CADisplayLink类对应的selector就会被调用一次。所以通常情况下,按照iOS设备屏幕的刷新率60次/秒
  • 延迟
    iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。但如果调用的方法比较耗时,超过了屏幕刷新周期,就会导致跳过若干次回调调用机会。
    如果CPU过于繁忙,无法保证屏幕60次/秒的刷新率,就会导致跳过若干次调用回调方法的机会,跳过次数取决CPU的忙碌程度。
  • 使用场景
    从原理上可以看出,CADisplayLink适合做界面的不停重绘,比如视频播放的时候需要不停地获取下一帧用于界面渲染。

重要属性

  • frameInterval
    NSInteger类型的值,用来设置间隔多少帧调用一次selector方法,默认值是1,即每帧都调用一次。
  • duration
    readOnly的CFTimeInterval值,表示两次屏幕刷新之间的时间间隔。需要注意的是,该属性在target的selector被首次调用以后才会被赋值。selector的调用间隔时间计算方式是:调用间隔时间 = duration × frameInterval。

GCD定时器

  • 一次性定时
   double delayInSeconds = 2.0;
   dispatch_time_t timer = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

   dispatch_after(timer, dispatch_get_main_queue(), ^(void){

        NSLog(@"GCD-----%@",[NSThread currentThread]);

    });

delayInSeconds:执行之前等待的时间。例设置成2.0,就代表2秒后执行方法

  • 重复执行
@interface ViewController ()
//  注意:此处应该使用强引用 strong
/** 定时器(这里不用带*,因为dispatch_source_t就是个类,内部已经包含了*) */
@property (nonatomic, strong) dispatch_source_t timer; 
@end
0.创建队列
    dispatch_queue_t queue = dispatch_get_main_queue();
//或者此方法
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);


1.创建GCD中的定时器
    /*
     第一个参数:创建source的类型 DISPATCH_SOURCE_TYPE_TIMER:定时器
     第二个参数:0
     第三个参数:0
     第四个参数:队列
     */
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);



2.设置时间等
    /*
     第一个参数:定时器对象
     第二个参数:DISPATCH_TIME_NOW 表示从现在开始计时
     第三个参数:间隔时间 GCD里面的时间最小单位为 纳秒
     第四个参数:精准度(表示允许的误差,0表示绝对精准)
     */
    dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);




3.要调用的任务
    dispatch_source_set_event_handler(self.timer, ^{
        NSLog(@"GCD-----%@",[NSThread currentThread]);

       dispatch_async(dispatch_get_main_queue(), ^{
         //回到主线程做事情
       });
    });

4.开始执行
    dispatch_resume(self.timer);


5.取消定时器
    dispatch_cancel(self.timer);
    self.timer = nil;
  • 注意

此处注意一定要强引用定时器 ,否则定时器执行到 } 后将会被释放,无定时效果。

  • 特性
  • GCD定时器不受RunLoop约束,比NSTimer更加准时
  • GCD定时器时间非常精准,最小的定时时间可以达到1纳秒,所以用在非常精确的定时场合

NSTimer和CADisplayLink 的区别和联系

1、原理不同
  • CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息, CADisplayLink类对应的selector就会被调用一次。
  • NSTimer以指定的模式注册到runloop后,每当设定的周期时间到达后,runloop会向指定的target发送一次指定的selector消息。
2、周期设置方式不同
  • iOS设备的屏幕刷新频率(FPS)是60Hz,因此CADisplayLink的selector默认调用周期是每秒60次,这个周期可以通过frameInterval属性设置,CADisplayLink的selector每秒调用次数=60/frameInterval。比如当frameInterval设为2,每秒调用就变成30次。因此,CADisplayLink周期的设置方式略显不便。
  • NSTimer的selector调用周期可以在初始化时直接设定,相对就灵活的多。
3、精确度不同
  • iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。
  • NSTimer的精确度就显得低了点,比如NSTimer的触发时间到的时候,runloop如果在忙于别的调用,触发时间就会推迟到下一个runloop周期。更有甚者,在OS X v10.9以后为了尽量避免在NSTimer触发时间到了而去中断当前处理的任务,NSTimer新增了tolerance属性,让用户可以设置可以容忍的触发的时间范围。
4、使用场合
  • 从原理上不难看出,CADisplayLink使用场合相对专一,适合做界面的不停重绘,比如视频播放的时候需要不停地获取下一帧用于界面渲染。
  • NSTimer的使用范围要广泛的多,各种需要单次或者循环定时处理的任务都可以使用。

有人说到,我是使用NSTimer和还是GCD技术呢,关于这个我就不做太详细的介绍了,希望大家看一下这篇文章,这里给我们说了我们如何使用他俩:http://www.jianshu.com/p/0c050af6c5ee

参考了一下这篇文字:http://www.jianshu.com/p/646ebb8b205e

相关文章

  • iOS Timer

    iOS开发中定时器经常会用到,iOS中常用的定时器有三种,分别是NSTime,CADisplayLink和GCD。...

  • NSTimer+Block

    在ios10以前,使用NSTimer的 会导致self被timer持有,如果timer不主动调用invalidat...

  • 01.Swift - Timer - block

    iOS10以后Timer新增了Timer.init()系列方法,这些方法都提供了block回调,非常好用,但是每次...

  • NSTimer在iOS9之前和iOS9之后释放的问题

    问题描述: 在iOS9及之前通过 _timer = [NSTimer scheduledTimerWithTime...

  • iOS NSTimer

    http://blog.callmewhy.com/2015/07/06/weak-timer-in-ios/ 今...

  • iOS swift timer管理

    我们知道在iOS中Timer很容易引起引用循环。原因大家都很清楚就是Timer在运行后不仅会被当前的target所...

  • iOS中的Timer Note

    iOS中的Timer Note NSTimer:NSTimer就是CFRunLoopTimerRef,一个NSTi...

  • ios run timer

    ios run timer 一直对iOS的runtimer机制不太理解,或者是理解有偏差,趁着最近不忙,认真的研究...

  • 预防 Timer 的循环引用

    在iOS开发过程中,Timer(NSTimer)是我们经常要使用的一个类。通过Timer,可以定时触发某个事件,或...

  • Crash拦截器 - NSTimer无法释放和内存泄漏之解除

    前言 在iOS开发中,我们使用定时器(timer)的几率很高,系统中最常用的方式有GCD中提供的timer接口和我...

网友评论

      本文标题:iOS Timer

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