SwiftUI DispatchQueue asyncAfter 在十个计划任务后停止正常工作

SwiftUI DispatchQueue asyncAfter stops working correctly after ten scheduled tasks

如果我 运行 下面的代码,它会正确计数到 10,然后跳过每个奇数。

如果让它 运行 超过 20,它将开始越来越多地跳过(输出将像 1-2-3-4-5-6-7-8-9-10- 12-14-16-18-20-23-26-29 ...).

找不到例如计划任务的限制,所以问你:) 从昨天开始学习SwiftUI,如果这是一个明显的问题,请原谅。

如果它被安排在与 main 不同的 DispatchQueue 中,则没有区别。

感谢您的帮助!

import SwiftUI

struct ContentView: View {
    @State private var counter : Int = 0
    
    func buttonTap() {
        let time : DispatchTime = .now()
        
        var delay : Double = 0
        
        for _ in 1...50 {
            
            delay = delay + 0.2
            
            DispatchQueue.main.asyncAfter(deadline: time + delay) {
                counter += 1
            }
            
        }
    }
    
    var body: some View {
        ZStack {
            
            Color(.black)
                .edgesIgnoringSafeArea(/*@START_MENU_TOKEN@*/.all/*@END_MENU_TOKEN@*/)
            
            Text(String(counter))
                .foregroundColor(.green)
                .padding()
            
            Text("Button")
                .foregroundColor(.green)
                .offset(y: 50)
                .onTapGesture(perform: {
                    buttonTap()
                })
            
        }
    }
}

这是一种奇怪的行为,甚至事件之间的时间间隔要长得多(例如 2 秒)。

考虑改用 Timer

您可以使用多个单独的触发计时器,它们的工作方式与您的单独调度一样:

func buttonTap() {
    
    var delay : Double = 0
    
    for _ in 1...50 {
        
        delay = delay + 0.2
        
        Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { timer in
            self.counter += 1
        }
        
    }
}

或每 0.2 秒触发一次的单个计时器:

func buttonTap() {
    Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true) { timer in
        self.counter += 1
        if counter == 50 {
            timer.invalidate()
        }
    }
}

此行为称为计时器合并,在这种情况下,发生在彼此 10% 以内的独立计划事件将合并在一起,并且将同时 运行。这是一项省电功能。

有多种解决方案可以避免合并:

  • 使用重复计时器;
  • 在前一次迭代的完成处理程序中安排每次迭代;或
  • 创建不会合并的“严格”GCD 计时器。

重复计时器是最常见的解决方案。