美文网首页iOS锦囊
iOS 三种常用定时器NSTimer、CADisplayLink

iOS 三种常用定时器NSTimer、CADisplayLink

作者: 雪碧童鞋 | 来源:发表于2018-01-24 14:54 被阅读28次

    在iOS开发过程中,我们常常需要在某个时间后执行某个方法,或者是按照某个周期一直执行某个方法,这时我们就需要用到定时器,而在iOS中,常用的定时器有以下三种NSTimerCADisplayLinkGCD,下面对这几种方法的创建、执行、销毁及优缺点做个详解:

    1. NSTimer

    创建方式

    //方式一
        self.timer = [NSTimer scheduledTimerWithTimeInterval:2
                                                      target:self
                                                    selector:@selector(timerTest)
                                                    userInfo:nil
                                                     repeats:YES];
    //方式二
        self.timer = [NSTimer timerWithTimeInterval:2
                                             target:self
                                           selector:@selector(timerTest)
                                           userInfo:nil
                                            repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
    
    • 使用 scheduled 开头创建的定时器,系统会自动的把它加入到 runLoopNSDefaultRunLoopMode 中,缺点是不能更改mode模式
    • 使用 timerWithTimeInterval 开头创建的定时器,需要手动的把他添加到runLoop中,同时设置它的mode运行模式。
    定时执行(以下就是我们设置定时器需要做的事件)
    - (void)timerTest {
        NSLog(@"==========NSTimer===============");
    }
    

    暂停、继续

    暂停
    self.timer.fireDate = [NSDate distantFuture];
    继续
    self.timer.fireDate = [NSDate distantPast];
    
    • 应用场景 比如,在页面消失的时候关闭定时器,然后等页面再次打开的时候,又开启定时器(主要是为了防止它在后台运行,占用CPU)

    销毁

      [self.timer invalidate];
      self.timer = nil;
    

    RunLoop 有五种运行模式,其中第1、5种为苹果对外提供的mode

    1. kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
    2. UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
    3. UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
    4. GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
    5. kCFRunLoopCommonModes: 这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一种真正的Mode
    
    • 常见问题
      在我们平时开发中可能遇到过,当我们使用NSTimer每一段时间执行一些事情时滑动UIScrollViewNSTimer就会暂停,当我们停止滑动以后,NSTimer又会重新恢复。那是因为Mode的切换造成的,当我们在主线程使用定时器,此时RunLoopModekCFRunLoopDefaultMode,即定时器属于kCFRunLoopDefaultMode,那么此时我们滑动ScrollView时,RunLoopMode会切换到UITrackingRunLoopMode,因此在主线程的定时器就不在管用了,调用的方法也就不再执行了,当我们停止滑动时,RunLoopMode切换回kCFRunLoopDefaultMode,所有NSTimer就又管用了。

    • 解决方案

    1. 将NSTimer添加到两种模式下
    2. 使用占位的运行模式 NSRunLoopCommonModes标记,凡是被打上NSRunLoopCommonModes标记的都可以运行,而kCFRunLoopDefaultModeUITrackingRunLoopMode就被打上commonModel标记。

    NSTimer 缺点

    计时不精确:不管是一次性的还是周期性的timer的实际触发事件的时间,都会与所加入的RunLoopRunLoop Mode有关,如果此RunLoop正在执行一个连续性的运算,timer就会被延时出发。重复性的timer遇到这种情况,如果延迟超过了一个周期,则会在延时结束后立刻执行,并按照之前指定的周期继续执行。

    2.CADisplayLink

    • 他是苹果专门提供的一个类,主要的优势在于他的执行频率是根据设备屏幕的刷新频率来计算的,也即是时间间隔最准确的定时器。

    创建方式

     self.displayLink = [CADisplayLink displayLinkWithTarget:self
                                                       selector:@selector(timerTest)];
     [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    

    定时执行

    - (void)timerTest {
        NSLog(@"==========CADisplayLink===============");
    }
    

    暂停、继续

    添加一个按钮用于控制暂停继续(或其他点击事件)
    pasued属性是控制计时器暂停与恢复,设置为YES的时候会暂停事件的触发。

    - (void) displayLinkStart {
        self.displayLink.paused = !self.displayLink.paused;
    }
    

    销毁

    - (void)stopDisplayLink {
        [self.displayLink invalidate];
        self.displayLink = nil;
    }
    

    优缺点

    • 优点: 依托于设备屏幕刷新频率触发事件,所以其触发时间上是最准确的。也是最适合做UI不断刷新的事件,过渡相对流畅,无卡顿感。

    • 缺点:

    1. 由于依托于屏幕刷新频率,若果CPU不堪重负而影响了屏幕刷新,那么我们的触发事件也会受到相应影响。
    2. selector触发的时间间隔只能是duration的整倍数
    3. selector事件如果大于其触发间隔就会造成掉帧现象。

    相关属性

     @property(readonly, nonatomic) CFTimeInterval timestamp;    //获取上一次selector被执行的时间戳
     @property(readonly, nonatomic) CFTimeInterval duration; //获取当前设备的屏幕刷新时间间隔
     @property(getter=isPaused, nonatomic) BOOL paused;  //控制计时器暂停与恢复的属性
        
        //事件触发间隔。是指两次selector触发之间间隔几次屏幕刷新,默认值为1,也就是说屏幕每刷新一次,执行一次selector,这个也可以间接用来控制动画速度
     @property(nonatomic) NSInteger frameInterval
        CA_AVAILABLE_BUT_DEPRECATED_IOS (3.1, 10.0, 9.0, 10.0, 2.0, 3.0, "use preferredFramesPerSecond");
     @property(nonatomic) NSInteger preferredFramesPerSecond;
    

    3. GCD-dispatch_source_t

    创建 添加 执行方法

    - (void)addDisPatch_source {
        //1.创建
        self.disTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
        //2.设置时间
        dispatch_source_set_timer(self.disTimer, dispatch_walltime(NULL,0 * NSEC_PER_SEC), 1 * NSEC_PER_SEC, 0);
        //3.执行
        dispatch_source_set_event_handler(self.disTimer, ^{
            NSLog(@"=========dispatch_source_t=========");
        });
    }
    

    参数:

    dispatch_source_create()相关参数

    参数 意义
    type dispatch源可处理的事件
    handle 可以理解为句柄、索引或id,假如要监听进程,需要传入进程的ID
    mask 可以理解为描述,提供更详细的描述,让它知道具体要监听什么
    queue 自定义源需要的一个队列,用来处理所有的响应句柄(block)

    dispatch_source_set_timer()相关参数

    参数 意义
    source dispatch_source_t
    start 事件首次触发的延迟时间
    interval 时间间隔
    leeway 误差范围

    开始 暂停

    //开始
    dispatch_resume(self.disTimer);
    //暂停
    dispatch_suspend(self.disTimer);
    

    销毁

    dispatch_source_cancel(self.disTimer);
    

    注意:dispatch_source_t 一定要被设置为成员变量,否则将会立即被释放。

    优缺点

    • 优点:不受当前runloopMode的影响。
    • 缺点:虽然不受runloopMode的影响,但是其计时效应仍不是百分之百准确的。

    相关文章

      网友评论

      本文标题:iOS 三种常用定时器NSTimer、CADisplayLink

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