美文网首页
iOS开发之一Timer

iOS开发之一Timer

作者: NanNan | 来源:发表于2022-02-17 17:57 被阅读0次

通过原理、精确度、例子来分析

一、原理

通俗解释:计时好比数数, 在iOS中, 数数的人是系统内核,内核会根据一些设定好的条件 (比如按时) 产生相应事件, 然后通过回调函数向外抛出 (可理解为"报时"). 我们通过注册观察者来监听取得这些回调, 从而达到计时的目的。

注意重点1:这些与时间相关的事件的载体叫做事件源 (Source), iOS中有两种 Source: Run Loop SourceDispatch Source.

1、 Run Loop Source会唤醒当前Run Loop, 然后执行回调函数.
关于Run Loop Source, 可参考之前的博文iOS开发之进阶篇(8)—— Run Loops.
2、 Dispatch Source也会产生一些特定的事件, 事件通过 block 自动加入到对应的 dispatch queue 中.
关于Dispatch Source, 可参考苹果文档.

注意重点2:也就是说, iOS中有两种计时方式, 分别对应Run LoopDispatch (GCD)中的两种计时器源.

1、 NSTimer (Timer)CADisplayLink 都属于 Run Loop Source.
2、 GCD 属于Dispatch Source.

二、精确度

1. NSTimer (Timer)

Run Loop中的计时器源会受到模式的影响, 如果模式不对则不会触发. 比如说添加到主线程中的NSTimer, 当滚动 Scroll View 的时候, 模式发生改变, NSTimer暂时失效.

而重复计时器会受到当前事务的影响, 可能在一定范围内产生偏差, 偏差大小则和计时器的tolerance属性有关.

如果触发时间延迟得太久, 以致错过了一个或多个计划的触发时间, 则 timer 将在错过的时间段内仅触发一次.

2. CADisplayLink

CADisplaylink 也是由Run Loop中的计时器源触发, 它与NSTimer相似, 都可以以一定的时间间隔触发回调 selector. 不同的是, CADisplaylink 的时间间隔是与屏幕的刷新频率相关联的.

CADisplayLink 的时间间隔由preferredFramesPerSecond属性来决定的, 意为每秒刷新的帧数. 如果设备的最大刷新率是每秒60帧, 则实际帧速率包括每秒15、20、30和60帧.

如果你设置为一个特定的值, 他么这个值最终也会和15、20、30以及60之间做最合适匹配. 比如设置为26或35, 则实际帧速率为30.

iOS设备的屏幕刷新频率是固定的, 因此CADisplayLink在正常情况下会在每次刷新结束都被调用, 精确度相当高. 但不要在CADisplayLink计时器中做耗时操作, 因为可能会被系统忽略掉.

3. GCD

由于GCD拥有强大的资源配置能力, 因此其计时器精确度是相当可观的. 而且GCD计时器不需添加到run loop中, 因此在子线程中也可直接使用.

使用GCD定时并不是说在指定时间后马上执行任务, 而是在指定时间后将任务添加到队列. 任务的执行则取决于当前的队列状态(串并行, 是否挂起等). 比如下面例子, 定时看似应该在2秒后执行, 结果却是5秒后:

    NSLog(@"start");

    dispatch_queue_t queue = dispatch_get_main_queue();

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0*NSEC_PER_SEC)), queue, ^{
        NSLog(@"time is up");
    });

    NSLog(@"end");

    sleep(5);

// ------------------------------------------------------
log:
2020-08-11 14:43:41.959636+0800 KKTimerDemo[2119:91835] start
2020-08-11 14:43:41.959804+0800 KKTimerDemo[2119:91835] end
2020-08-11 14:43:46.978124+0800 KKTimerDemo[2119:91835] time is up

1、NSTimer (Timer)

#import "ViewController.h"


@interface ViewController ()

@property (nonatomic, strong)   NSTimer *mainTimer;
@property (nonatomic, strong)   NSTimer *globalTimer;

@end


@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 在主线程中创建NSTimer
    [self createTimerInMain];
    
    // 在子线程中创建NSTimer
    [self createTimerInGlobal];
}

- (void)dealloc
{
    // 销毁定时器
    [self cancelMainTimer];
    [self cancelGlobalTimer];
}

#pragma mark - 创建

// 在主线程中创建NSTimer
- (void)createTimerInMain {
    
    self.mainTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(mainTimeIsUp:) userInfo:nil repeats:YES];
//    [self.mainTimer fire];  // 立即执行
}

// 在子线程中创建NSTimer
- (void)createTimerInGlobal {
   
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 法一
        self.globalTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(globalTimeIsUp:) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] run];
        
        // 法二
//        self.globalTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(globalTimeIsUp:) userInfo:nil repeats:YES];
//        [[NSRunLoop currentRunLoop] addTimer:self.globalTimer forMode:NSRunLoopCommonModes];    // Common Mode 不受 Scroll View 滚动影响
//        [[NSRunLoop currentRunLoop] run];
    });
}

#pragma mark - 销毁

// 取消 mainTimer
- (void)cancelMainTimer {
    
    if (_mainTimer) {
        [self.mainTimer invalidate];
        self.mainTimer = nil;
    }
}

// 取消 globalTimer
- (void)cancelGlobalTimer {
    
    if (_globalTimer) {
        [self.globalTimer invalidate];
        self.globalTimer = nil;
    }
}

#pragma mark - 定时时间到

// mainTimer 定时时间到
- (void)mainTimeIsUp:(NSTimer *)timer {
    
    NSLog(@"mainTimeIsUp");
}

// globalTimer 定时时间到
- (void)globalTimeIsUp:(NSTimer *)timer {
    
    NSLog(@"globalTimeIsUp");
}


@end

2、CADisplayLink

#pragma mark - CADisplayLink

- (void)createTimerUseCADisplayLink {
    
    self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLick)];
    self.link.preferredFramesPerSecond = 60;
    [self.link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}


- (void)cancelTimer {
    
    if (_link) {
        [self.link invalidate];
        self.link = nil;
    }
}


- (void)displayLick {
    
    NSLog(@"displayLick");
}

3、GCD

@property (nonatomic, strong)   dispatch_source_t  gcdTimer;

- (void)createGCDTimer {
    
    // 定时一次
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"dispatch_after");
    });
    
    // 循环
    self.gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
    // 1:计时器源  2:开始时间  3:间隔  4:误差
    dispatch_source_set_timer(self.gcdTimer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    dispatch_source_set_event_handler(self.gcdTimer, ^{
        // do something
        NSLog(@"dispatch_event_handler");
    });
    dispatch_resume(self.gcdTimer);     // 开启定时器, 立即执行 dispatch_source_set_event_handler 里任务
}


- (void)cancelGCDTimer {
    
    if(_gcdTimer){
        dispatch_source_cancel(self.gcdTimer);
        self.gcdTimer = nil;
    }
}

好文章值得分享:
1、iOS开发之进阶篇(10)—— Timer

相关文章

  • iOS开发之一Timer

    通过原理、精确度、例子来分析 一、原理 通俗解释:计时好比数数, 在iOS中, 数数的人是系统内核,内核会根据一些...

  • 预防 Timer 的循环引用

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

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

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

  • iOS 中的 timer 任务(寻找内存恶鬼之旅)

    前言 在 iOS 的开发过程中定时任务中能找到使用的场景,然而在 iOS 中默认的有关 timer 的 api ...

  • iOS14开发-Timer

    定时器可以设置按固定周期执行某个操作。iOS 中主要有 3 种定时器,本文先讲解第一种 Timer。 使用 iOS...

  • iOS 定时器耗电探究

    iOS开发中的几种定时器 iOS开发中定时器实现方式大致有三种,一种是Timer实现,一种是通过GCD自己创建,另...

  • iOS Timer

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

  • NSTimer的循环引用问题解决方案

    iOS开发中,针对循环引用的问题,会有很多方面,block,代理,自循环,多循环,还有一个就是Timer的循环引用...

  • NSTimer+Block

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

  • Android Timer、CountDownTimer、Ala

    一、Timer TimerTask 参考Java中的Timer和TimerTask在Android中的用法 在开发...

网友评论

      本文标题:iOS开发之一Timer

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