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

2019-03-14  本文已影响0人  Nulll

一、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;
        }
    });

}

上一篇下一篇

猜你喜欢

热点阅读