更可靠和高精度的 iOS 定時(shí)器

定時(shí)器一般用于延遲一段時(shí)間執(zhí)行特定的代碼,必要的話按照指定的頻率重復(fù)執(zhí)行。iOS 中延時(shí)執(zhí)行有多種方式,常用的有:

  • NSTimer
  • NSObject 的 (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
  • CADisplayLink
  • GCD 的 dispatch_after
  • GCD 的 dispatch_source_t

每種方法創(chuàng)建的定時(shí)器,其可靠性與最小精度都有不同。可靠性指是否嚴(yán)格按照設(shè)定的時(shí)間間隔按時(shí)執(zhí)行,最小精度指支持的最小時(shí)間間隔是多少。

1. NSRunLoop

談到定時(shí)器,首先需要了解的一個(gè)概念是 NSRunLoop。NSRunLoop 是消息處理的一種機(jī)制,類似于 Windows 中的消息循環(huán),有個(gè)更通用的叫法是 Event Loop。

其原理很簡(jiǎn)單,啟動(dòng)一個(gè)循環(huán),無(wú)限地重復(fù)接受消息->等待消息->處理消息這個(gè)過(guò)程,直到退出。偽代碼如下:

void loop() {
    do {
        void *msg = getMessage();
        processMessage(msg);
    } while (msg != quit);
}

每個(gè)線程內(nèi)部都會(huì)有一個(gè) RunLoop,啟動(dòng) RunLoop 之后,就能夠讓線程在沒(méi)有消息時(shí)休眠,在有消息時(shí)被喚醒并處理消息,避免資源長(zhǎng)期被占用。

在 iOS 中,NSThead 和 NSRunLoop 是一一對(duì)應(yīng)的,但創(chuàng)建線程的時(shí)候不會(huì)默認(rèn)創(chuàng)建 NSRunLoop,實(shí)際上也不允許自己創(chuàng)建 NSRunLoop,在線程內(nèi)第一次調(diào)用[NSRunLoop currentRunLoop]的時(shí)候才會(huì)自動(dòng)創(chuàng)建。

1.1 NSRunLoop 處理的輸入源(input sources):

  • 鼠標(biāo)、鍵盤事件。
  • NSPort 對(duì)象。
  • NSConnection 對(duì)象。

NSRunLoop 也處理 NSTimer 事件,但 NSTimer 并不屬于輸入源的一種。

1.2 蘋果使用 NSRunLoop 實(shí)現(xiàn)的功能:

  • 硬件操作,如觸摸、按鍵、搖晃等。
  • 手勢(shì)操作。
  • 界面刷新,如更新了 UI 的 frame,或手動(dòng)調(diào) setNeedsLayout/setNeedsDisplay。
  • 定時(shí)器。包括 NSTimer、CADisplayLink、PerformSelecter、GCD。
  • 網(wǎng)絡(luò)請(qǐng)求。

深入了解 RunLoop有更深入完整的介紹。

2. NSTimer

最常用,能滿足對(duì)間隔要求不嚴(yán)格、對(duì)精確度不敏感的需求。

2.1 使用方法

- (void)startNSTimer {
    self.timer = [NSTimer scheduledTimerWithTimeInterval:self.timeInterval target:self selector:@selector(onNSTimerTimeout:) userInfo:nil repeats:YES];
}

- (void)onNSTimerTimeout:(id)sender {
    NSLog(@"onNSTimerTimeout");
}

2.2 可靠性

不可靠,其所在的 RunLoop 會(huì)定時(shí)檢測(cè)是否可以觸發(fā) NSTimer 的事件,但由于 iOS 有多個(gè) RunLoop 的運(yùn)行模式,如果被切到另一個(gè) run loop,NSTimer 就不會(huì)被觸發(fā)。每個(gè) RunLoop 的循環(huán)間隔也無(wú)法保證,當(dāng)某個(gè)任務(wù)耗時(shí)比較久,RunLoop 的下一個(gè)消息處理就只能順延,導(dǎo)致 NSTimer 的時(shí)間已經(jīng)到達(dá),但 Runloop 卻無(wú)法及時(shí)觸發(fā) NSTimer,導(dǎo)致該時(shí)間點(diǎn)的回調(diào)被錯(cuò)過(guò)。

蘋果官方文檔:

A timer is not a real-time mechanism; it fires only when one of the run loop modes to which the timer has been added is running and able to check if the timer’s firing time has passed. If a timer’s firing time occurs during a long callout or while the run loop is in a mode that is not monitoring the timer, the timer does not fire until the next time the run loop checks the timer.

2.3 最小精度

理論上最小精度為 0.1 毫秒。不過(guò)由于受 Runloop 的影響,會(huì)有 50 ~ 100 毫秒的誤差,所以,實(shí)際精度可以認(rèn)為是 0.1 秒。

蘋果官方文檔:

Because of the various input sources a typical run loop manages, the effective resolution of the time interval for a timer is limited to on the order of 50-100 milliseconds.

2.4 實(shí)測(cè)結(jié)果

間隔 0.1 秒,調(diào)用12次。其中倒數(shù)第二次調(diào)用前會(huì)執(zhí)行一個(gè)比較耗時(shí)的運(yùn)算任務(wù)。

代碼:

- (void)startNSTimer {
    [self setupConfig];

    [self runNSTimerIfNeeded];

    NSLog(@"NSTimer start with interval: %.3f ms, start time: %@, total count: %d", self.timeInterval * 1000, [self timeStringWithTime:self.startTime], (int)self.maxCount);
}

- (void)runNSTimerIfNeeded {
    self.timer = [NSTimer scheduledTimerWithTimeInterval:self.timeInterval
                                     target:self
                                   selector:@selector(onNSTimerTimeout:)
                                   userInfo:nil
                                    repeats:NO];

    self.startTime = [NSDate date];
}

- (void)onNSTimerTimeout:(NSTimer *)sender {
    NSLog(@"%d, %@", ++ self.curCount, [self diffTimeStringFromStart]);

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

    if (self.curCount < self.maxCount) {
        [self runNSTimerIfNeeded];
        [self runBusyTaskIfNeeded];
    }
}

結(jié)果:

2016-08-29 11:32:40.302 TimerDemo[37258:10736148] NSTimer start with interval: 100.000 ms, start time: 1472441560302.602 ms, total count: 12
2016-08-29 11:32:40.403 TimerDemo[37258:10736148] 1, interval: 101.045 ms, discrepancy: 1.045 ms
2016-08-29 11:32:40.505 TimerDemo[37258:10736148] 2, interval: 100.890 ms, discrepancy: 0.890 ms
2016-08-29 11:32:40.606 TimerDemo[37258:10736148] 3, interval: 101.087 ms, discrepancy: 1.087 ms
2016-08-29 11:32:40.707 TimerDemo[37258:10736148] 4, interval: 101.038 ms, discrepancy: 1.038 ms
2016-08-29 11:32:40.809 TimerDemo[37258:10736148] 5, interval: 101.061 ms, discrepancy: 1.061 ms
2016-08-29 11:32:40.910 TimerDemo[37258:10736148] 6, interval: 101.069 ms, discrepancy: 1.069 ms
2016-08-29 11:32:41.012 TimerDemo[37258:10736148] 7, interval: 101.031 ms, discrepancy: 1.031 ms
2016-08-29 11:32:41.113 TimerDemo[37258:10736148] 8, interval: 101.035 ms, discrepancy: 1.035 ms
2016-08-29 11:32:41.214 TimerDemo[37258:10736148] 9, interval: 100.890 ms, discrepancy: 0.890 ms
2016-08-29 11:32:41.315 TimerDemo[37258:10736148] 10, interval: 101.042 ms, discrepancy: 1.042 ms
2016-08-29 11:32:41.315 TimerDemo[37258:10736148] start busy task
2016-08-29 11:32:41.970 TimerDemo[37258:10736148] finish busy task
2016-08-29 11:32:41.970 TimerDemo[37258:10736148] 11, interval: 654.319 ms, discrepancy: 554.319 ms
2016-08-29 11:32:42.071 TimerDemo[37258:10736148] 12, interval: 100.906 ms, discrepancy: 0.906 ms

可以看到偏差在 1 ~ 2 毫秒左右。在第 10 次之后執(zhí)行了一個(gè)較耗時(shí)的任務(wù),導(dǎo)致第 11 次比預(yù)期延遲了 0.5 秒執(zhí)行。后面的回調(diào)仍然按照預(yù)設(shè)的延時(shí)執(zhí)行。

3. performSelector:withObject:afterDelay:

這是 NSObject 對(duì) NSTimer 封裝后提供的一個(gè)比較簡(jiǎn)單的延時(shí)方法,內(nèi)部用的也是 NSTimer,所以,同上。

4. CADisplayLink

CADisplayLink 也可以用作定時(shí)器,其調(diào)用間隔與屏幕刷新頻率一致,也就是每秒 60 幀,間隔 16.67 ms。與 NSTimer 類似,如果在兩次屏幕刷新之間執(zhí)行了一個(gè)比較耗時(shí)的任務(wù),其中的某一幀就會(huì)被跳過(guò),造成 UI 卡頓。

4.1 使用方法

- (void)runCADisplayLinkTimer {
    CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onCADisplayLinkTimeout)];
    displayLink.frameInterval = 0.0167; // S
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    self.displayLink = displayLink;
}

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

4.2 可靠性

如果執(zhí)行的任務(wù)很耗時(shí),也會(huì)導(dǎo)致回調(diào)被錯(cuò)過(guò),所以并不十分可靠。但是,假如調(diào)用者能夠確保任務(wù)能夠在最小時(shí)間間隔內(nèi)執(zhí)行完成,CADisplayLink 就比較可靠,因?yàn)槠聊坏乃⑿骂l率是固定的。

4.3 最小精度

受限于每秒 60 幀的屏幕刷新頻率,注定 CADisplayLink 的最小精度為 16.67 毫秒。誤差在 1 毫秒左右。

另外需要注意的是,雖然 CADisplayLink 有一個(gè)屬性 frameInterval 是用于設(shè)置定時(shí)器的調(diào)用間隔,但是這個(gè)屬性會(huì)在第一次回調(diào)之后才生效,對(duì)于第一次回調(diào),總是會(huì)以 1/60 的間隔來(lái)執(zhí)行的。這樣會(huì)導(dǎo)致的結(jié)果是,比如你設(shè)置了每 1 秒執(zhí)行一次某個(gè)方法,但是第一次執(zhí)行的時(shí)候,卻是在 16.7 毫秒之后,遠(yuǎn)遠(yuǎn)小于預(yù)設(shè)值。

4.4 實(shí)測(cè)結(jié)果

間隔 0.1 秒,調(diào)用12次。其中倒數(shù)第二次調(diào)用前會(huì)執(zhí)行一個(gè)比較耗時(shí)的運(yùn)算任務(wù)。

代碼:

- (void)startCADisplayLinkTimer {
    [self setupConfig];

    [self runCADisplayLinkTimer];

    NSLog(@"CADisplayLink start with interval: %.3f ms, start time: %@, total count: %d", self.timeInterval * 1000, [self timeStringWithTime:self.startTime], (int)self.maxCount);
}

- (void)runCADisplayLinkTimer {
    CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onCADisplayLinkTimeout)];
    NSInteger frameInterval = floor(self.timeInterval * 1000 / (1000 / 60.0));
    displayLink.frameInterval = frameInterval;

    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    self.displayLink = displayLink;

    self.startTime = [NSDate date];
}

- (void)onCADisplayLinkTimeout {
    NSLog(@"%d, %@", ++ self.curCount, [self diffTimeStringFromStart]);

    if (self.curCount < self.maxCount) {
        self.startTime = [NSDate date];
        [self runBusyTaskIfNeeded];
    } else {
        [self.displayLink invalidate];
        self.displayLink = nil;
    }
}

結(jié)果:

2016-08-29 11:33:47.835 TimerDemo[37258:10736148] CADisplayLink start with interval: 100.000 ms, start time: 1472441627835.872 ms, total count: 12
2016-08-29 11:33:47.845 TimerDemo[37258:10736148] 1, interval: 10.061 ms, discrepancy: -89.939 ms
2016-08-29 11:33:47.946 TimerDemo[37258:10736148] 2, interval: 99.829 ms, discrepancy: -0.171 ms
2016-08-29 11:33:48.046 TimerDemo[37258:10736148] 3, interval: 99.573 ms, discrepancy: -0.427 ms
2016-08-29 11:33:48.145 TimerDemo[37258:10736148] 4, interval: 99.427 ms, discrepancy: -0.573 ms
2016-08-29 11:33:48.246 TimerDemo[37258:10736148] 5, interval: 99.801 ms, discrepancy: -0.199 ms
2016-08-29 11:33:48.346 TimerDemo[37258:10736148] 6, interval: 99.754 ms, discrepancy: -0.246 ms
2016-08-29 11:33:48.446 TimerDemo[37258:10736148] 7, interval: 99.791 ms, discrepancy: -0.209 ms
2016-08-29 11:33:48.546 TimerDemo[37258:10736148] 8, interval: 99.836 ms, discrepancy: -0.164 ms
2016-08-29 11:33:48.646 TimerDemo[37258:10736148] 9, interval: 99.840 ms, discrepancy: -0.160 ms
2016-08-29 11:33:48.746 TimerDemo[37258:10736148] 10, interval: 99.811 ms, discrepancy: -0.189 ms
2016-08-29 11:33:48.746 TimerDemo[37258:10736148] start busy task
2016-08-29 11:33:49.399 TimerDemo[37258:10736148] finish busy task
2016-08-29 11:33:49.400 TimerDemo[37258:10736148] 11, interval: 653.891 ms, discrepancy: 553.891 ms
2016-08-29 11:33:49.412 TimerDemo[37258:10736148] 12, interval: 12.566 ms, discrepancy: -87.434 ms

除了第一次回調(diào),間隔誤差比較大之外,別的回調(diào)誤差在 0.1 ~ 0.5 毫秒之間,精度比 NSTimer 要高。第 11 次回調(diào),受耗時(shí)任務(wù)影響,延時(shí)了 0.5 秒。值得注意的是,第 12 次,延時(shí)再次與第一次回調(diào)一樣,變成了 1/60 秒左右。

換言之,CADisplayLink 在第一次回調(diào)以及在耗時(shí)任務(wù)之后的回調(diào),精度不可控。

5. GCD dispatch_after

dispatch_after 用起來(lái)十分簡(jiǎn)單,代碼緊湊易讀,而且可以很輕松地在別的線程分配延時(shí)任務(wù),所以使用范圍很廣泛。

5.1 使用方法

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    //handle timeout
});

5.2 可靠性

Any fire of the timer may be delayed by the system in order to improve power consumption and system performance. The upper limit to the allowable delay may be configured with the ‘leeway’ argument, the lower limit is under the control of the system.

5.3 最小精度

延時(shí)參數(shù)的單位是納秒。如果有延時(shí),則無(wú)法保證。

5.4 實(shí)測(cè)結(jié)果

間隔 0.1 秒,調(diào)用12次。其中倒數(shù)第二次調(diào)用前會(huì)執(zhí)行一個(gè)比較耗時(shí)的運(yùn)算任務(wù)。

代碼:

- (void)startDispatchAfterTimer {
    [self setupConfig];

    [self runDispatchAfterTimerIfNeeded];

    NSLog(@"DispatchAfterTimer start with interval: %.3f ms, start time: %@, total count: %d", self.timeInterval * 1000, [self timeStringWithTime:self.startTime], (int)self.maxCount);
}

- (void)runDispatchAfterTimerIfNeeded {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.timeInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self onDispatchAfterTimeout];
    });

    self.startTime = [NSDate date];
}

- (void)onDispatchAfterTimeout {
    NSLog(@"%d, %@", ++ self.curCount, [self diffTimeStringFromStart]);
    if (self.curCount < self.maxCount) {
        [self runDispatchAfterTimerIfNeeded];
        [self runBusyTaskIfNeeded];
    }
}

結(jié)果:

2016-08-29 11:34:09.652 TimerDemo[37258:10736148] DispatchAfterTimer start with interval: 100.000 ms, start time: 1472441649652.825 ms, total count: 12
2016-08-29 11:34:09.756 TimerDemo[37258:10736148] 1, interval: 103.876 ms, discrepancy: 3.876 ms
2016-08-29 11:34:09.866 TimerDemo[37258:10736148] 2, interval: 109.686 ms, discrepancy: 9.686 ms
2016-08-29 11:34:09.976 TimerDemo[37258:10736148] 3, interval: 109.772 ms, discrepancy: 9.772 ms
2016-08-29 11:34:10.085 TimerDemo[37258:10736148] 4, interval: 108.764 ms, discrepancy: 8.764 ms
2016-08-29 11:34:10.195 TimerDemo[37258:10736148] 5, interval: 109.057 ms, discrepancy: 9.057 ms
2016-08-29 11:34:10.299 TimerDemo[37258:10736148] 6, interval: 104.544 ms, discrepancy: 4.544 ms
2016-08-29 11:34:10.408 TimerDemo[37258:10736148] 7, interval: 108.753 ms, discrepancy: 8.753 ms
2016-08-29 11:34:10.516 TimerDemo[37258:10736148] 8, interval: 107.597 ms, discrepancy: 7.597 ms
2016-08-29 11:34:10.626 TimerDemo[37258:10736148] 9, interval: 109.933 ms, discrepancy: 9.933 ms
2016-08-29 11:34:10.736 TimerDemo[37258:10736148] 10, interval: 109.791 ms, discrepancy: 9.791 ms
2016-08-29 11:34:10.736 TimerDemo[37258:10736148] start busy task
2016-08-29 11:34:11.394 TimerDemo[37258:10736148] finish busy task
2016-08-29 11:34:11.394 TimerDemo[37258:10736148] 11, interval: 657.669 ms, discrepancy: 557.669 ms
2016-08-29 11:34:11.496 TimerDemo[37258:10736148] 12, interval: 102.005 ms, discrepancy: 2.005 ms

平均誤差 9 毫秒。

6. GCD dispatch_source_t

經(jīng)測(cè)試,dispatch_source_t 的最小精度和可靠性都與 diapatch_after 差不多。

6.1 實(shí)測(cè)結(jié)果

間隔 0.1 秒,調(diào)用12次。其中倒數(shù)第二次調(diào)用前會(huì)執(zhí)行一個(gè)比較耗時(shí)的運(yùn)算任務(wù)。

代碼:

- (void)startDispatchSourceTimer {
    [self setupConfig];

    [self runDispatchSourceTimerIfNeeded];

    NSLog(@"DispatchSourceTimer start with interval: %.3f ms, start time: %@, total count: %d", self.timeInterval * 1000, [self timeStringWithTime:self.startTime], (int)self.maxCount);
}

- (void)runDispatchSourceTimerIfNeeded {
    self.sourceTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());

    dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, 0);
    dispatch_source_set_timer(self.sourceTimer, start, (int64_t)(self.timeInterval * NSEC_PER_SEC), 0);

    dispatch_source_set_event_handler(self.sourceTimer, ^{
        [self onDispatchSourceTimeout];
    });

    dispatch_resume(self.sourceTimer);

    self.startTime = [NSDate date];
}

- (void)onDispatchSourceTimeout {
    NSLog(@"%d, %@", ++ self.curCount, [self diffTimeStringFromStart]);

    dispatch_cancel(self.sourceTimer);
    self.timer = nil;

    if (self.curCount < self.maxCount) {
        [self runDispatchAfterTimerIfNeeded];
        [self runBusyTaskIfNeeded];
    }
}

結(jié)果:

2016-08-29 11:34:24.088 TimerDemo[37258:10736148] DispatchSourceTimer start with interval: 100.000 ms, start time: 1472441664088.390 ms, total count: 12
2016-08-29 11:34:24.089 TimerDemo[37258:10736148] 1, interval: 1.429 ms, discrepancy: -98.571 ms
2016-08-29 11:34:24.196 TimerDemo[37258:10736148] 2, interval: 106.696 ms, discrepancy: 6.696 ms
2016-08-29 11:34:24.306 TimerDemo[37258:10736148] 3, interval: 109.500 ms, discrepancy: 9.500 ms
2016-08-29 11:34:24.416 TimerDemo[37258:10736148] 4, interval: 109.999 ms, discrepancy: 9.999 ms
2016-08-29 11:34:24.526 TimerDemo[37258:10736148] 5, interval: 109.744 ms, discrepancy: 9.744 ms
2016-08-29 11:34:24.636 TimerDemo[37258:10736148] 6, interval: 109.691 ms, discrepancy: 9.691 ms
2016-08-29 11:34:24.746 TimerDemo[37258:10736148] 7, interval: 109.767 ms, discrepancy: 9.767 ms
2016-08-29 11:34:24.856 TimerDemo[37258:10736148] 8, interval: 109.799 ms, discrepancy: 9.799 ms
2016-08-29 11:34:24.966 TimerDemo[37258:10736148] 9, interval: 109.820 ms, discrepancy: 9.820 ms
2016-08-29 11:34:25.076 TimerDemo[37258:10736148] 10, interval: 109.804 ms, discrepancy: 9.804 ms
2016-08-29 11:34:25.076 TimerDemo[37258:10736148] start busy task
2016-08-29 11:34:25.734 TimerDemo[37258:10736148] finish busy task
2016-08-29 11:34:25.734 TimerDemo[37258:10736148] 11, interval: 657.591 ms, discrepancy: 557.591 ms
2016-08-29 11:34:25.835 TimerDemo[37258:10736148] 12, interval: 101.295 ms, discrepancy: 1.295 ms

從結(jié)果看,與 diapatch_after 區(qū)別不大。

7. 更高精度的定時(shí)器

上述的各種定時(shí)器,都受限于蘋果為了保護(hù)電池和提高性能采用的策略,導(dǎo)致無(wú)法實(shí)時(shí)地執(zhí)行回調(diào)。如果你的確需要使用更高精度的定時(shí)器,官方也提供了方法,見(jiàn) High Precision Timers in iOS / OS X

前面所述的定時(shí)器,使用方法各有不同,但其核心代碼實(shí)際上是一樣的。

There are many API’s in iOS and OS X that allow waiting for a specified period of time. They may be Objective C or C, and they take different kinds of arguments, but they all end up using the same code inside the kernel.

而有別于普通定時(shí)器的高精度定時(shí)器,則是基于高優(yōu)先級(jí)的線程調(diào)度類創(chuàng)建的定時(shí)器,在沒(méi)有多線程沖突的情況下,這類定時(shí)器的請(qǐng)求會(huì)被優(yōu)先處理。

7.1 實(shí)現(xiàn)方法

  • 把定時(shí)器所在的線程,移到高優(yōu)先級(jí)的線程調(diào)度類。
  • 使用更精確的計(jì)時(shí)器API,換言之,你想要 10 秒后執(zhí)行,就絕對(duì)在 10 秒后執(zhí)行,絕不提前,也不延遲。

7.2 如何使用

提高調(diào)度優(yōu)先級(jí):

#include <mach/mach.h>
#include <mach/mach_time.h>
#include <pthread.h>

void move_pthread_to_realtime_scheduling_class(pthread_t pthread) {
    mach_timebase_info_data_t timebase_info;
    mach_timebase_info(&timebase_info);

    const uint64_t NANOS_PER_MSEC = 1000000ULL;
    double clock2abs = ((double)timebase_info.denom / (double)timebase_info.numer) * NANOS_PER_MSEC;

    thread_time_constraint_policy_data_t policy;
    policy.period      = 0;
    policy.computation = (uint32_t)(5 * clock2abs); // 5 ms of work
    policy.constraint  = (uint32_t)(10 * clock2abs);
    policy.preemptible = FALSE;

    int kr = thread_policy_set(pthread_mach_thread_np(pthread_self()),
                   THREAD_TIME_CONSTRAINT_POLICY,
                   (thread_policy_t)&policy,
                   THREAD_TIME_CONSTRAINT_POLICY_COUNT);
    if (kr != KERN_SUCCESS) {
        mach_error("thread_policy_set:", kr);
        exit(1);
    }
}

精確延時(shí):

#include <mach/mach.h>
#include <mach/mach_time.h>

static const uint64_t NANOS_PER_USEC = 1000ULL;
static const uint64_t NANOS_PER_MILLISEC = 1000ULL * NANOS_PER_USEC;
static const uint64_t NANOS_PER_SEC = 1000ULL * NANOS_PER_MILLISEC;

static mach_timebase_info_data_t timebase_info;

static uint64_t abs_to_nanos(uint64_t abs) {
    return abs * timebase_info.numer  / timebase_info.denom;
}

static uint64_t nanos_to_abs(uint64_t nanos) {
    return nanos * timebase_info.denom / timebase_info.numer;
}

void example_mach_wait_until(int argc, const char * argv[]) {
    mach_timebase_info(&timebase_info);
    uint64_t time_to_wait = nanos_to_abs(10ULL * NANOS_PER_SEC);
    uint64_t now = mach_absolute_time();
    mach_wait_until(now + time_to_wait);
}

7.3 最小精度

小于 0.5 毫秒。這里有一份實(shí)現(xiàn)的代碼以及與普通定時(shí)器的對(duì)比。

8. 參考

原文地址:http://blog.lessfun.com/blog/2016/08/05/reliable-timer-in-ios/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容