在 "for in loop" 中使用延迟 DispatchQueue

Using delay DispatchQueue in "for in loop"

任务是每秒改变一次背景颜色。被使用 "for in loop"。对于延迟,使用了 DispatchQueue。一切似乎都很好,但注意到在 10 次迭代后,背景颜色开始变化,延迟 2 秒,稍晚 3 秒。迭代次数越多,延迟越大。我在控制台中显示时间(秒)以查看它是如何变化的。我看到了结果,但我不明白哪里出了问题。我是通过timer做的任务,没有问题,但是我想了解DispatchQueue延迟有什么问题。

for i in 1...150 {

    DispatchQueue.main.asyncAfter(deadline: .now() + Double(i)) {

        self.view.backgroundColor = UIColor(red: .random(in: 0...1),
                                          green: .random(in: 0...1), 
                                           blue: .random(in: 0...1), 
                                          alpha: 1)
         print("\(ymd)")

         ymd = self.myCalendar.dateComponents([.second], from: Date())
    }
}

控制台:

second: 21 isLeapMonth: false 
second: 21 isLeapMonth: false 
second: 22 isLeapMonth: false 
second: 23 isLeapMonth: false 
second: 24 isLeapMonth: false 
second: 25 isLeapMonth: false 
second: 26 isLeapMonth: false 
second: 27 isLeapMonth: false 
second: 28 isLeapMonth: false 
second: 29 isLeapMonth: false 
second: 30 isLeapMonth: false 
second: 32 isLeapMonth: false 
second: 33 isLeapMonth: false 
second: 33 isLeapMonth: false 
second: 35 isLeapMonth: false 
second: 35 isLeapMonth: false 
second: 37 isLeapMonth: false 
second: 37 isLeapMonth: false 
second: 39 isLeapMonth: false 
second: 39 isLeapMonth: false 
second: 41 isLeapMonth: false 
second: 41 isLeapMonth: false 
second: 44 isLeapMonth: false 
second: 44 isLeapMonth: false 
second: 44 isLeapMonth: false 
second: 47 isLeapMonth: false 
second: 47 isLeapMonth: false 
second: 47 isLeapMonth: false 
second: 50 isLeapMonth: false 
second: 50 isLeapMonth: false 
second: 50 isLeapMonth: false 
second: 54 isLeapMonth: false 
second: 54 isLeapMonth: false 
second: 54 isLeapMonth: false 
second: 57 isLeapMonth: false 
second: 57 isLeapMonth: false 
second: 57 isLeapMonth: false 
second: 57 isLeapMonth: false 
second: 1 isLeapMonth: false 
second: 1 isLeapMonth: false 
second: 1 isLeapMonth: false 
second: 1 isLeapMonth: false 
second: 6 isLeapMonth: false 
second: 6 isLeapMonth: false 
second: 6 isLeapMonth: false 
second: 6 isLeapMonth: false 
second: 6 isLeapMonth: false 
second: 11 isLeapMonth: false 
second: 11 isLeapMonth: false 
second: 11 isLeapMonth: false 
second: 11 isLeapMonth: false 
second: 11 isLeapMonth: false 
second: 17 isLeapMonth: false 
second: 17 isLeapMonth: false 
second: 17 isLeapMonth: false 
second: 17 isLeapMonth: false 
second: 17 isLeapMonth: false 

这是“定时器合并”的结果,其中调度块的“余地”是延迟的 10%,最多一分钟的余地。 (这是 buried in the libdispatch code。)这是一项节能功能,可以同时将 coalesce/group 远距离、独立计划的任务 运行,以避免不必要地多次启动硬件。避免这种合并的最简单方法是使用重复计时器:

var counter = 0

Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
    counter += 1

    guard let self = self, counter <= 150 else {
        timer.invalidate()
        return
    }

    self.view.backgroundColor = UIColor(red: .random(in: 0...1),
                                      green: .random(in: 0...1),
                                       blue: .random(in: 0...1),
                                      alpha: 1)

    let ymd = self.myCalendar.dateComponents([.second], from: Date())
    print(ymd)
}

注意 [weak self] 模式的使用,以避免让计时器保持对视图控制器的持久引用。使用那个 guard 语句,如果视图控制器被关闭,我们将 invalidate 计时器。

顺便说一句,如果您想让颜色变化不那么刺耳,请为变化设置动画:

var counter = 0

Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
    counter += 1
    guard let self = self, counter <= 150 else {
        timer.invalidate()
        return
    }

    UIView.animate(withDuration: 0.1) {
        self.view.backgroundColor = UIColor(red: .random(in: 0...1),
                                          green: .random(in: 0...1),
                                           blue: .random(in: 0...1),
                                          alpha: 1)
    }

    ...
}