美文网首页
iOS 中关于定时器的一些理解

iOS 中关于定时器的一些理解

作者: Nulll | 来源:发表于2019-03-14 11:25 被阅读0次

    一、NSTimer 关于定时器的时间误差问题。

    1)创建一个循环定时器并且执行循环操作。

    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.00 target:self selector:@selector(timerRun) userInfo:nil repeats:YES];
    
    - (void)timerRun {
        NSLog(@"timerTest");
    }
    

    打印结果如下:


    屏幕快照 2019-03-14 上午11.11.30.png

    这里的打印可以看出时间上有一定的误差,虽然这个误差已经很小了。但误差确实存在。。
    如果在循环里面加上计算任务,

    - (void)timerRun { 
        NSLog(@"timerTest");
        int count = 0;
        for (long i = 0; i < 1000000000; i++) {
            count += 1;
        }
    
    

    再来看看打印的结果,


    屏幕快照 2019-03-14 上午11.15.32.png

    现在误差就很明显了。

    2)原因分析:

    定时器被添加在主线程中,由于定时器在一个RunLoop中被检测一次,所以如果在这一次的RunLoop中做了耗时的操作,当前RunLoop持续的时间超过了定时器的间隔时间,那么下一次定时就被延后了。
    RunLoop 有三个模式,NSDefaultRunLoopMode; 默认模式
    UITrackingRunLoopMode; UI滚动模式
    NSRunLoopCommonModes; 上面两种模式的共同模式

    NSTimer默认是添加在RunLoop的 NSDefaultRunLoopMode 模式中。
    所以要将 NSTimer 添加在 NSRunLoopCommonModes 模式中;

    二、GCD timer

        //获得队列
        dispatch_queue_t dispatchQueue = dispatch_get_global_queue(0, 0);
        //创建一个gcd定时器 (dispatch_source_t timer:dispatch_source_t本质还是个OC对象,是个局部变量,需要强引用,不然下面使用无效)
        self.gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatchQueue);
        //OC对象%@可以打印
        NSLog(@"%@", self.gcdTimer);
        //设置定时器的各种属性(几时开始,每隔多长时间执行一次)
        //GCD时间参数,一般是纳秒 NSEC_PER_SEC 纳秒单位等于一秒;DISPATCH_TIME_NOW当前时间
        dispatch_time_t start = DISPATCH_TIME_NOW;
        uint64_t interval = (uint64_t)(2.0 *NSEC_PER_SEC);
        dispatch_source_set_timer(self.gcdTimer, start, interval, 0);
        dispatch_source_set_event_handler(self.gcdTimer, ^{
    
            [self timerRun];
        });
        //启动定时器
        dispatch_resume(self.gcdTimer);
    
    - (void)timerRun {
        
        NSLog(@"timerTest");
        int count = 0;
        for (long i = 0; i < 1000000000; i++) {
            count += 1;
        }
    }
    

    GCD Timer 同样也会有一样的误差。


    屏幕快照 2019-03-14 下午4.49.19.png

    三、解决办法

    1)将NStimer 添加在子线程中执行。然后在需要UI操作的地方放置在主线程里面。

    dispatch_queue_t queue = dispatch_queue_create("const_char_dispatch_timer", DISPATCH_QUEUE_CONCURRENT);
        __weak typeof(self)ws = self;
        dispatch_async(queue, ^{
                __strong typeof(ws)ss = ws;
                ss.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerRun) userInfo:nil repeats:YES];
                [[NSRunLoop currentRunLoop] addTimer:ss.timer forMode:NSRunLoopCommonModes];
                [[NSRunLoop currentRunLoop] run];
    
        });
    - (void)timerRun {
        
        NSLog(@"timerTest");
        dispatch_queue_t queue = dispatch_queue_create("const_char_dispatch_timer", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
    
            int count = 0;
            for (long i = 0; i < 1000000000; i++) {
                count += 1;
            }
        });
    
    }
    
    

    相关文章

      网友评论

          本文标题:iOS 中关于定时器的一些理解

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