iOS Swift Task.sleep 准确性

问题描述 投票:0回答:1

我正在研究 Swift 中可取消的异步延迟,它向后兼容 iOS 15

Task {
    let before = Date.timeIntervalSinceReferenceDate
    
    let seconds: TimeInterval = 100
    print("> sleeping for \(seconds) seconds...")
    
    try await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
    
    let after = Date.timeIntervalSinceReferenceDate
    
    print(String(format: "> slept for %0.3f seconds", after - before))
}

使用

Task.sleep
的解决方案效果很好,但对于较长的延迟来说似乎相当不准确。

在 iOS 16 模拟器上运行会产生以下结果:

> sleeping for 20.0 seconds...
> slept for 21.332 seconds

> sleeping for 60.0 seconds...
> slept for 63.995 seconds

> sleeping for 100.0 seconds...
> slept for 106.662 seconds

有没有更好的方法来实现这个目标?

ios swift concurrency delay sleep
1个回答
0
投票
func accurateDelay(seconds: TimeInterval) async {
    await withCheckedContinuation { continuation in
        let queue = DispatchQueue.global(qos: .default)
        let timer = DispatchSource.makeTimerSource(queue: queue)
        
        timer.schedule(deadline: .now() + seconds, leeway: .nanoseconds(1))
        
        timer.setEventHandler {
            continuation.resume()
            timer.cancel()
        }
        
        timer.activate()
    }
}
我认为

A

DispatchSourceTimer
可以用来更准确地执行某些操作。下面的例子给了我一些非常准确的信息。

struct AccurateTimerView: View {
    let delay: TimeInterval
    
    var body: some View {
        Button(action: {
            Task {
                let before = Date.timeIntervalSinceReferenceDate
                await accurateDelay(seconds: delay)
                let after = Date.timeIntervalSinceReferenceDate
                print(String(format: "> slept for %0.3f seconds", after - before))
            }
        }, label: {
            Text("Start")
        })
    }
}

#Preview {
    VStack {
        AccurateTimerView(delay: 20)
        AccurateTimerView(delay: 60)
        AccurateTimerView(delay: 100)
    }
}

> slept for 20.001 seconds
> slept for 60.000 seconds
> slept for 99.999 seconds
© www.soinside.com 2019 - 2024. All rights reserved.