Swift 中严格调度的循环计时

c_b*_*oth 5 nstimer nsrunloop ios swift

以非常严格的时间安排重复任务的最佳方法是什么(对于音乐排序足够准确和可靠)?从 Apple 文档中,很明显 NSTimer 在这个意义上是不可靠的(即“计时器不是实时机制”)。我从 AudioKit 的AKPlaygroundLoop借用的一种方法在大约 4 毫秒内似乎是一致的(如果不是很准确),并且可能是可行的:

class JHLoop: NSObject{
    var trigger: Int {
        return Int(60 * duration)  // 60fps * t in seconds
    }
    var counter: Int = 0
    var duration: Double  = 1.0 // in seconds, but actual loop is ~1.017s
    var displayLink: CADisplayLink?
    weak var delegate: JHLoopDelegate?

    init(dur: Double) {
        duration = dur
    }

    func stopLoop() {
        displayLink?.invalidate()
    }

    func startLoop() {
        counter = 0
        displayLink = CADisplayLink(target: self, selector: "update")
        displayLink?.frameInterval = 1
        displayLink?.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSRunLoopCommonModes)
    }

    func update() {
        if counter < trigger {
            counter++
        } else {
            counter = 0

            // execute loop here
            NSLog("loop executed")
            delegate!.loopBody()
        }
    }
}
protocol JHLoopDelegate: class {
    func loopBody()
}    
Run Code Online (Sandbox Code Playgroud)

↑ 用我暂时尝试使用的实际类替换了代码。

作为参考,我希望制作一个多节奏鼓音序器,所以一致性是最重要的。我还需要能够实时平滑地修改循环,最好是循环周期。

有一个更好的方法吗?

Den*_*ert 4

您可以尝试使用 mach_wait_until() api。它\xe2\x80\x99s非常适合高精度定时器。我从这里稍微改变了苹果的例子。它在我的命令行工具项目中运行良好。在下面的代码片段中,我将main()方法从我的项目更改为startLoop(). 你也可以看到这个。\n希望有帮助。

\n\n
static const uint64_t NANOS_PER_USEC = 1000ULL;\nstatic const uint64_t NANOS_PER_MILLISEC = 1000ULL * NANOS_PER_USEC;\nstatic const uint64_t NANOS_PER_SEC = 1000ULL * NANOS_PER_MILLISEC;\nstatic mach_timebase_info_data_t timebase_info;\n\nstatic uint64_t nanos_to_abs(uint64_t nanos) {\n    return nanos * timebase_info.denom / timebase_info.numer;\n}\n\nfunc startLoop() {\n        while(true) { // \n            int64_t nanosec = waitSomeTime(1000); // each second\n            NSLog(@"%lld", nanosec);\n        update() // call needed update here \n        }\n}\n\nuint64_t waitSomeTime(int64_t eachMillisec) {\n    uint64_t        start;\n    uint64_t        end;\n    uint64_t        elapsed;\n    uint64_t        elapsedNano;\n\n    if ( timebase_info.denom == 0 ) {\n        (void) mach_timebase_info(&timebase_info);\n    }\n\n    // Start the clock.\n    start = mach_absolute_time();\n\n    mach_wait_until(start + nanos_to_abs(eachMillisec * NANOS_PER_MILLISEC));\n\n    // Stop the clock.\n    end = mach_absolute_time();\n\n    // Calculate the duration.\n    elapsed = end - start;\n    elapsedNano = elapsed * timebase_info.numer / timebase_info.denom;\n\n    return elapsedNano;\n}\n
Run Code Online (Sandbox Code Playgroud)\n