iOS中的三大定时器


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

NSTimer

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

方式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会自动将创建的定时器以默认方式添加到当前线程runloop中,而无需手动添加。但是在此种模式下,当滚动屏幕时runloop会进入另外一种模式,定时器会暂停,为了解决这种问题,可以像方式2那样把定时器添加到NSRunLoopCommonModes模式下。

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

[time fire];

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方法

调用时机

CADisplayLink是一个和屏幕刷新率同步的定时器类。CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息,CADisplayLink类对应的selector就会被调用一次,所以可以使用CADisplayLink做一些和屏幕操作相关的操作。

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

GCD定时器

一次性定时
dispatch_time_t timer = dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC);

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

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

重复执行的定时器
@property (nonatomic ,strong)dispatch_source_t timer;//  注意:此处应该使用强引用 strong
{
    //0.创建队列 dispatch_queue_t queue = dispatch_get_main_queue(); //1.创建GCD中的定时器 /* 第一个参数:创建source的类型 DISPATCH_SOURCE_TYPE_TIMER:定时器 第二个参数:0 第三个参数:0 第四个参数:队列 */ dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); //2.设置时间等 /* 第一个参数:定时器对象 第二个参数:DISPATCH_TIME_NOW 表示从现在开始计时 第三个参数:间隔时间 GCD里面的时间最小单位为 纳秒 第四个参数:精准度(表示允许的误差,0表示绝对精准) */ dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC); //3.要调用的任务 dispatch_source_set_event_handler(timer, ^{ NSLog(@"GCD-----%@",[NSThread currentThread]); }); //4.开始执行 dispatch_resume(timer); // self.timer = timer; }

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

GCD定时器时间非常精准,最小的定时时间可以达到1纳秒,所以用在非常精确的定时场合。




GCD定时器

  • GCD定时器不受RunLoop约束,比NSTimer更加准时
  • 证明,实现GCD定时器

<code class="objectivec" style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; font-size: 12px; border: none; background-color: transparent;"><span class="hljs-class"><span style="color: rgb(133, 153, 0);">@interface</span> <span class="hljs-title" style="color: rgb(181, 137, 0);">ViewController</span> ()</span> <span style="color: rgb(147, 161, 161);">/** 定时器(这里不用带*,因为dispatch_source_t就是个类,内部已经包含了*) */</span> <span style="color: rgb(133, 153, 0);">@property</span> (<span style="color: rgb(133, 153, 0);">nonatomic</span>, <span style="color: rgb(133, 153, 0);">strong</span>) dispatch_source_t timer; <span style="color: rgb(133, 153, 0);">@end</span></code>



<code class="cpp" style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; font-size: 12px; border: none; background-color: transparent;">    <span  style="color: rgb(133, 153, 0);">int</span> count = <span  style="color: rgb(42, 161, 152);">0</span>;

     <span  style="color: rgb(147, 161, 161);">// 获得队列</span>
    <span  style="color: rgb(133, 153, 0);">dispatch_queue_t</span> <span  style="color: rgb(38, 139, 210);">queue</span> = dispatch_get_main_queue();

    <span  style="color: rgb(147, 161, 161);">// 创建一个定时器(dispatch_source_t本质还是个OC对象)</span>
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, <span  style="color: rgb(42, 161, 152);">0</span>, <span  style="color: rgb(42, 161, 152);">0</span>, <span  style="color: rgb(38, 139, 210);">queue</span>);

    <span  style="color: rgb(147, 161, 161);">// 设置定时器的各种属性(几时开始任务,每隔多长时间执行一次)</span>
    <span  style="color: rgb(147, 161, 161);">// GCD的时间参数,一般是纳秒(1秒 == 10的9次方纳秒)</span>
    <span  style="color: rgb(147, 161, 161);">// 何时开始执行第一个任务</span>
    <span  style="color: rgb(147, 161, 161);">// dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC) 比当前时间晚3秒</span>
    <span  style="color: rgb(133, 153, 0);">dispatch_time_t</span> start = dispatch_time(DISPATCH_TIME_NOW, (<span  style="color: rgb(133, 153, 0);">int64_t</span>)(<span  style="color: rgb(42, 161, 152);">1.0</span> * NSEC_PER_SEC));
    <span  style="color: rgb(133, 153, 0);">uint64_t</span> interval = (<span  style="color: rgb(133, 153, 0);">uint64_t</span>)(<span  style="color: rgb(42, 161, 152);">1.0</span> * NSEC_PER_SEC);
    dispatch_source_set_timer(self.timer, start, interval, <span  style="color: rgb(42, 161, 152);">0</span>);

    <span  style="color: rgb(147, 161, 161);">// 设置回调</span>
    dispatch_source_set_event_handler(self.timer, ^{
        NSLog(@<span  style="color: rgb(42, 161, 152);">"------------%@"</span>, [NSThread currentThread]);
        count++;

        <span  style="color: rgb(133, 153, 0);">if</span> (count == <span  style="color: rgb(42, 161, 152);">4</span>) {
            <span  style="color: rgb(147, 161, 161);">// 取消定时器</span>
            dispatch_cancel(self.timer);
            self.timer = nil;
        }
    });

    <span  style="color: rgb(147, 161, 161);">// 启动定时器</span>
    dispatch_resume(self.timer);</code>









GCD定时器

  • GCD定时器不受RunLoop约束,比NSTimer更加准时
  • 证明,实现GCD定时器
<code class="objectivec" style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; font-size: 12px; border: none; background-color: transparent;"><span class="hljs-class"><span  style="color: rgb(133, 153, 0);">@interface</span> <span class="hljs-title" style="color: rgb(181, 137, 0);">ViewController</span> ()</span>
<span  style="color: rgb(147, 161, 161);">/** 定时器(这里不用带*,因为dispatch_source_t就是个类,内部已经包含了*) */</span>
<span  style="color: rgb(133, 153, 0);">@property</span> (<span  style="color: rgb(133, 153, 0);">nonatomic</span>, <span  style="color: rgb(133, 153, 0);">strong</span>) dispatch_source_t timer;
<span  style="color: rgb(133, 153, 0);">@end</span></code>



<code class="cpp" style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; font-size: 12px; border: none; background-color: transparent;">    <span  style="color: rgb(133, 153, 0);">int</span> count = <span  style="color: rgb(42, 161, 152);">0</span>;

     <span  style="color: rgb(147, 161, 161);">// 获得队列</span>
    <span  style="color: rgb(133, 153, 0);">dispatch_queue_t</span> <span  style="color: rgb(38, 139, 210);">queue</span> = dispatch_get_main_queue();

    <span  style="color: rgb(147, 161, 161);">// 创建一个定时器(dispatch_source_t本质还是个OC对象)</span>
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, <span  style="color: rgb(42, 161, 152);">0</span>, <span  style="color: rgb(42, 161, 152);">0</span>, <span  style="color: rgb(38, 139, 210);">queue</span>);

    <span  style="color: rgb(147, 161, 161);">// 设置定时器的各种属性(几时开始任务,每隔多长时间执行一次)</span>
    <span  style="color: rgb(147, 161, 161);">// GCD的时间参数,一般是纳秒(1秒 == 10的9次方纳秒)</span>
    <span  style="color: rgb(147, 161, 161);">// 何时开始执行第一个任务</span>
    <span  style="color: rgb(147, 161, 161);">// dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC) 比当前时间晚3秒</span>
    <span  style="color: rgb(133, 153, 0);">dispatch_time_t</span> start = dispatch_time(DISPATCH_TIME_NOW, (<span  style="color: rgb(133, 153, 0);">int64_t</span>)(<span  style="color: rgb(42, 161, 152);">1.0</span> * NSEC_PER_SEC));
    <span  style="color: rgb(133, 153, 0);">uint64_t</span> interval = (<span  style="color: rgb(133, 153, 0);">uint64_t</span>)(<span  style="color: rgb(42, 161, 152);">1.0</span> * NSEC_PER_SEC);
    dispatch_source_set_timer(self.timer, start, interval, <span  style="color: rgb(42, 161, 152);">0</span>);

    <span  style="color: rgb(147, 161, 161);">// 设置回调</span>
    dispatch_source_set_event_handler(self.timer, ^{
        NSLog(@<span  style="color: rgb(42, 161, 152);">"------------%@"</span>, [NSThread currentThread]);
        count++;

        <span  style="color: rgb(133, 153, 0);">if</span> (count == <span  style="color: rgb(42, 161, 152);">4</span>) {
            <span  style="color: rgb(147, 161, 161);">// 取消定时器</span>
            dispatch_cancel(self.timer);
            self.timer = nil;
        }
    });

    <span  style="color: rgb(147, 161, 161);">// 启动定时器</span>
    dispatch_resume(self.timer);</code>