Как выйти из al oop, который использует asyn c DispatchQueue внутри - PullRequest
1 голос
/ 27 мая 2020

Я использую for l oop в сочетании с DispatchQueue с asyn c, чтобы постепенно увеличивать громкость воспроизведения в течение 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 будет продолжать печатать в течение каждого периода. Как лучше всего выйти из этого для l oop, использующего DispatchQueue.main.asyncAfter?

Обновлено: как заметил Роб, имеет смысл использовать 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()

Ответы [ 2 ]

3 голосов
/ 27 мая 2020

Вы не хотите использовать asyncAfter: хотя вы можете использовать воспроизведение DispatchWorkItem (которое можно отменить), вы получите беспорядок, пытаясь отслеживать все отдельные рабочие элементы. Хуже того, ряд отдельных отправляемых элементов будет подвергаться «объединению таймера», когда последние задачи начнут объединяться вместе, больше не срабатывая с желаемым интервалом.

Простое решение - использовать повторение Timer, что позволяет избежать слияния и легко аннулируется, когда вы хотите его остановить.

1 голос
/ 27 мая 2020

Вы можете использовать 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
    }
}
...