尝试用GCD创建一个精确的计时器

2024-01-02

我正在尝试制作一个需要非常精确的计时器的音乐应用程序(它需要与背景音乐同步)。我还需要在用户界面上将计时器显示为进度条。

我最初是从NSTimer,事实证明根本不准确,关闭时间超过 20 毫秒。我转向 GCD。但我似乎也无法让它发挥作用。

这是我的代码(删除了不相关的部分)

这是我的 ViewController 类中的第一行

var dispatchQueue = dispatch_queue_t()

而在我的override func viewDidLoad()功能

dispatchQueue = dispatch_queue_create("myQueueId", nil)

然后是使用调度队列的实际函数

// Generate new measure
// In the main dispatch queue because it handles a lot of work
func newMeasure() {
    timerBar.progress = 1.0
    // Reset the timer
    logEnd = NSDate()
    let lapse = logEnd.timeIntervalSinceDate(logStart)
    println("Lapse: \(lapse)")
    logStart = NSDate()

    // measureTimer is set to 1.47, so the newMeasure is supposedly to be called every 1.47 seconds
    timer = measureTimer

    startTime = NSDate()
    dispatch_async(dispatchQueue, {
        self.gameTimer()
    })

    // ... do a lot of stuff that will drag the timer if not put in dispatch queue
}

// Accurate Game Timer
// In the dispacheQueue so the timer can has its own thread
func gameTimer() {
    // Get the time now
    let now = NSDate()
    let adjust = now.timeIntervalSinceDate(self.startTime)
    // Refresh the start time to be now
    self.startTime = now
    self.timer = self.timer - adjust

    // Go back to the main dispatch queue to update UI
    dispatch_async(dispatch_get_main_queue(), {
        self.timerBar.progress = Float(self.timer / self.measureTimer)
    })

    if (self.timer <= 0.2) {
        dispatch_async(dispatch_get_main_queue(), {
            // Going back to the main dispatch queue to start another new measure
            NSTimer.scheduledTimerWithTimeInterval(self.timer, target: self, selector: Selector("newMeasure"), userInfo: nil, repeats: false)
        })
    }
    else {
        dispatch_after(dispatch_time_t(
            100 * NSEC_PER_MSEC), dispatchQueue, {
                self.gameTimer()
        })
    }
}

但实际的日志是这样的:

Lapse: 1.47152501344681
Lapse: 1.51420003175735
Lapse: 1.47065001726151
Lapse: 1.47149801254272
Lapse: 1.471755027771
Lapse: 1.47201299667358
Lapse: 1.47167503833771
Lapse: 1.47148901224136
Lapse: 1.47146201133728
Lapse: 1.47289103269577
Lapse: 1.47580003738403
Lapse: 1.47087097167969
Lapse: 1.47286003828049
Lapse: 1.47235900163651
Lapse: 1.47159999608994
Lapse: 1.47144496440887
Lapse: 1.50616401433945
Lapse: 1.51679295301437
Lapse: 1.47380495071411
Lapse: 1.47130501270294
Lapse: 1.50468301773071
Lapse: 1.4718160033226
Lapse: 1.49480104446411
Lapse: 1.50195497274399
Lapse: 1.50018000602722
Lapse: 1.47339296340942
Lapse: 1.47266495227814

他们仍然不那么准时。因为时间对音乐非常敏感,所以这是行不通的。 有人可以帮我解决这个准确性问题吗? 谢谢


你应该考虑退休NSTimer一起创建一个调度计时器,它有一个回旋余地参数,您可以在其中指定给予计时器运行的自由度。另外,如果您尝试在后台线程上执行计时器,这是一种更简单、更有效的方法:

private var timer: dispatch_source_t!
private var start: CFAbsoluteTime!

func startTimer() {
    let queue = dispatch_queue_create("com.domain.app.timer", nil)
    timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue)
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, UInt64(1.47 * Double(NSEC_PER_SEC)), 0) // every 1.47 seconds, with no leeway
    dispatch_source_set_event_handler(timer) {
        let current = CFAbsoluteTimeGetCurrent()
        NSLog(String(format: "%.4f", current - self.start))
        self.start = current
    }

    start = CFAbsoluteTimeGetCurrent()
    dispatch_resume(timer)
}

func stopTimer() {
    dispatch_source_cancel(timer)
    timer = nil
}

这不会恰好每 1.47 秒发生一次,但可能更接近。

请注意,我避免来回发送东西和/或安排新的计时器或dispatch_after, 尽管。我创建了一个调度计时器并放开它。另外,我也在回避NSDate并使用CFAbsoluteTime,这应该更有效。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

尝试用GCD创建一个精确的计时器 的相关文章

随机推荐