SwiftUI DispatchQueue asyncAfter在十个预定任务后停止正常工作

时间:2020-11-03 12:00:47

标签: swift swiftui grand-central-dispatch

如果我运行下面的代码,它将正确计数到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 个答案:

答案 0 :(得分:1)

这是一种奇怪的行为,甚至在事件之间的间隔时间更长(例如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()
        }
    }
}

答案 1 :(得分:1)

此行为称为计时器合并,其中在彼此之间10%之内发生的独立计划的事件将被合并在一起,并将同时运行。这是一项省电功能。

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

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

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