为什么减少间隔不会加速iOS计时器执行?

时间:2016-05-10 18:33:42

标签: ios swift timer grand-central-dispatch

当我运行此计时器代码持续60秒/ 1秒间隔或6秒/ .1秒间隔时,它按预期工作(完成速度提高10倍)。但是,将值减小到0.6秒/ .01秒并不会像预期的那样加快整体操作(使其完成另外10倍的速度)。

当我将此值设置为小于0.1时,它无法按预期工作:

// The interval to use
let interval: NSTimeInterval = 0.01 // 1.0 and 0.1 work fine, 0.01 does not

其余的相关代码(完整的操场在这里:donut builder gist):

// Extend NSTimeInterval to provide the conversion functions.
extension NSTimeInterval {

    var nSecMultiplier: Double {
        return Double(NSEC_PER_SEC)
    }

    public func nSecs() -> Int64 {
        return Int64(self * nSecMultiplier)
    }

    public func nSecs() -> UInt64 {
        return UInt64(self * nSecMultiplier)
    }

    public func dispatchTime() -> dispatch_time_t {
        // Since the last parameter takes an Int64, the version that returns an Int64 is used.
        return dispatch_time(DISPATCH_TIME_NOW, self.nSecs())
    }
}

// Define a simple function for getting a timer dispatch source.
func repeatingTimerWithInterval(interval: NSTimeInterval, leeway: NSTimeInterval, action: dispatch_block_t) -> dispatch_source_t {

    let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue())
    guard timer != nil else { fatalError() }

    dispatch_source_set_event_handler(timer, action)

    // This function takes the UInt64 for the last two parameters
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, interval.nSecs(), leeway.nSecs())

    dispatch_resume(timer)

    return timer
}

// Create the timer
let timer = repeatingTimerWithInterval(interval, leeway: 0.0) { () -> Void in

    drawDonut()
}

// Turn off the timer after a few seconds
dispatch_after((interval * 60).dispatchTime(), dispatch_get_main_queue()) { () -> Void in
    dispatch_source_cancel(timer)

    XCPlaygroundPage.currentPage.finishExecution()
}

2 个答案:

答案 0 :(得分:1)

无法保证为计时器设置的时间间隔。它只是一个目标。系统定期检查活动计时器并将其目标点火时间与当前时间进行比较,如果点火时间已经过去,它将触发计时器。但无法保证系统检查计时器的速度有多快。因此,目标间隔越短,线程正在做的其他工作越多,计时器的准确性就越低。来自Apple的文档:

  

计时器不是实时机制;它只在其中一个发射时发射   已添加计时器的运行循环模式正在运行且能够运行   检查计时器的开火时间是否已经过去。因为各种各样   输入源一个典型的运行循环管理,有效的分辨率   计时器的时间间隔限制在50-100的量级   毫秒。如果在长时间标注期间发生计时器的发射时间或   当运行循环处于不监视计时器的模式时,   在下次运行循环检查计时器之前,计时器不会触发。   因此,计时器可能发射的实际时间可能是   在计划的射击时间之后很长一段时间。

答案 1 :(得分:0)

这确实似乎是游乐场的限制。在实际的iOS设备上进行测试时,我可以达到0.01秒的间隔。

虽然我在关于运行循环速度限制的初步答案中错了 - 但GCD显然能够在幕后工作,以便允许在每次运行循环迭代时触发多个调度源。

然而,话虽如此,您仍然应该考虑iOS设备屏幕刷新的最快速度是每秒60次,或者每0.0167秒一次。

因此,更快地进行绘图更新毫无意义。您应该考虑使用CADisplayLink来同步绘图与屏幕刷新率 - 并调整绘图进度而不是计时器频率,以控制进度。

相当简陋的设置可能如下所示:

var displayLink:CADisplayLink?
var deltaTime:CFTimeInterval = 0
let timerDuration:CFTimeInterval = 5

func startDrawing() {

    displayLink?.invalidate()
    deltaTime = 0

    displayLink = CADisplayLink(target: self, selector: #selector(doDrawingUpdate))
    displayLink?.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
}

func doDrawingUpdate() {

    if deltaTime >= timerDuration {
        deltaTime = timerDuration
        displayLink?.invalidate()
        displayLink = nil
    }

    draw(CGFloat(deltaTime/timerDuration))
    deltaTime += displayLink?.duration ?? 0
}

func draw(progress:CGFloat) {
    // do drawing
}

通过这种方式,您可以确保以最大可用帧率绘制,如果设备处于拉紧状态且运行循环因此运行较慢,则绘制进度不会受到影响。