当我运行此计时器代码持续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()
}
答案 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
}
通过这种方式,您可以确保以最大可用帧率绘制,如果设备处于拉紧状态且运行循环因此运行较慢,则绘制进度不会受到影响。