ios

OC中定时器的三种实现方式

2017-05-30  本文已影响409人  Nevermind

NSTimer

NSTimer 常用的两种创建方式

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;

第一种方式

通过 timerWith… 方法创建的 timer ,需要手动添加到 runloop 中,否则不会启动。

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

第二种方式

通过 scheduleTimerWith… 方法创建的 timer,会默认被添加到 runloop 的NSDefaultRunLoopMode中,我们可以手动修改 runloop 的模式。

NSTimer *timer = [NSTimer timerWithTimeInterval:3.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

timer不准的原因

模式的改变

主线程的 runloop 里有两个预置的Mode:NSDefaultRunLoopModeUITrackingRunLoopMode
当创建一个 timer 并添加到 defaultMode 时,timer会得到重复回调。但此时滑动一个scrollview,RunLoop 会将 mode 切换到 UITrackingRunLoopMode,这时 timer 不会被回调,并且也不会影响到滑动操作。所以会导致NSTimer不准的情况。

解决方案:
定时器添加到 runloop 的 NSRunLoopCommonModes模式中,该模式是以上两种模式的组合。

线程阻塞

timer 触发时,如果 runloop 在阻塞状态,timer 的触发会推迟到下一个 runloop 周期,导致延迟。

dispatch_source 实现高精度定时器

GCD 实现的定时器不受 runloop 模式的影响,使用 dispatch_source 能够实现没有延迟的定时器。

    dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue1);
    self.timer = timer;
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
    dispatch_resume(timer);

CADisplayLink

能保证和屏幕刷新率相同的频率,将特定的内容画到屏幕上。
CADisplayLink 以特定的模式的添加到 runloop 后,每当屏幕需要刷新时,runloop 就会像 target 发送 selector 消息。所以 displayLink 的时间间隔是和屏幕刷新频率相关联的。

CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(test)];
[link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
上一篇下一篇

猜你喜欢

热点阅读