Swift中严格安排的循环时序

时间:2016-03-02 02:22:23

标签: ios swift nstimer nsrunloop

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

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()
}    

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

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

有更好的方法吗?

1 个答案:

答案 0 :(得分:2)

您可以尝试使用mach_wait_until()api。这对于高精度计时器来说非常好。我稍微改变了here的例子。它在我的命令行工具项目中工作正常。在下面的代码段中,我将main()方法从我的项目更改为startLoop()。你也可以看到this。 希望它有所帮助。

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 nanos_to_abs(uint64_t nanos) {
    return nanos * timebase_info.denom / timebase_info.numer;
}

func startLoop() {
        while(true) { // 
            int64_t nanosec = waitSomeTime(1000); // each second
            NSLog(@"%lld", nanosec);
        update() // call needed update here 
        }
}

uint64_t waitSomeTime(int64_t eachMillisec) {
    uint64_t        start;
    uint64_t        end;
    uint64_t        elapsed;
    uint64_t        elapsedNano;

    if ( timebase_info.denom == 0 ) {
        (void) mach_timebase_info(&timebase_info);
    }

    // Start the clock.
    start = mach_absolute_time();

    mach_wait_until(start + nanos_to_abs(eachMillisec * NANOS_PER_MILLISEC));

    // Stop the clock.
    end = mach_absolute_time();

    // Calculate the duration.
    elapsed = end - start;
    elapsedNano = elapsed * timebase_info.numer / timebase_info.denom;

    return elapsedNano;
}