如何打破内部使用异步 DispatchQueue 的循环

How to break out of a loop that uses async DispatchQueue inside

我正在使用 for 循环和带有异步功能的 DispatchQueue,以在 5 或 10 分钟的持续时间内逐渐增加播放量。

我目前的实现方式是:

    for i in (0...(numberOfSecondsToFadeOut*timesChangePerSecond)) {
        DispatchQueue.main.asyncAfter(deadline: .now() + Double(i)/Double(timesChangePerSecond)) {
            if self.activityHasEnded {
                NSLog("Activity has ended") //This will keep on printing
            } else {
                let volumeSetTo = originalVolume - (reductionAmount)*Float(i)
                self.setVolume(volumeSetTo)
            }
        }

        if self.activityHasEnded {
            break
        }
    }

我的目标是让 activityHasEnded 充当断路器。评论中指出的问题是,尽管使用 break,但 NSLog 将在每个时期继续打印。完全摆脱使用 DispatchQueue.main.asyncAfter 的 for 循环的更好方法是什么?

更新:如 Rob 所述,使用 Timer 更有意义。这是我所做的:

    self.fadeOutTimer = Timer.scheduledTimer(withTimeInterval: timerFrequency, repeats: true) { (timer) in
        let currentVolume = self.getCurrentVolume()
        if currentVolume > destinationVolume {
            let volumeSetTo = currentVolume - reductionAmount
            self.setVolume(volumeSetTo)
            print ("Lowered volume to \(volumeSetTo)")
        }
    }

当不再需要计时器时,我调用self.fadeOutTimer?.invalidate()

您可以使用 DispatchWorkItem,它可以异步发送到 DispatchQueue,也可以在发送后取消。

for i in (0...(numberOfSecondsToFadeOut*timesChangePerSecond)) {
    let work = DispatchWorkItem {
        if self.activityHasEnded {
            NSLog("Activity has ended") //This will keep on printing
        } else {
            let volumeSetTo = originalVolume - (reductionAmount)*Float(i)
            self.setVolume(volumeSetTo)
        }
    }
    DispatchQueue.main.asyncAfter(deadline: .now() + Double(i)/Double(timesChangePerSecond), execute: work)
    if self.activityHasEnded {
        work.cancel() // cancel the async work
        break // exit the loop
    }
}

您不想使用 asyncAfter:虽然您可以使用 DispatchWorkItem 再现(可取消),但您最终会在尝试跟踪所有个体时陷入混乱工作项目。更糟糕的是,一系列单独调度的项目将受到“计时器合并”的影响,其中后面的任务将开始聚集在一起,不再以所需的间隔触发。

简单的解决方案是使用重复 Timer,这样可以避免合并,并且在您想停止合并时很容易失效。