Лаги в пользовательском интерфейсе при использовании таймера

Я использую таймер, повторяющийся каждую секунду, чтобы обновить проигрывание currentPlaybackTime, так что я обновляю свой лейбл каждую секунду с прогрессом текущей песни. Я использовал NSTimer или в Свифте Timer повторять каждую секунду, однако у меня возникли проблемы при смене песни или запуске и остановке песни, метка секунд задерживалась (она оставалась неизменной в течение 5 секунд, пока вы слышите воспроизведение песни, а затем обновлялась до нужного времени). Затем я продолжил использовать GCD, и у меня возникла та же проблема, даже в фоновой очереди. А потом я пошел использовать DispatchSourceTimer и получил следующие журналы:

Fired: 2018-03-18 13:22:17 +0000
Timer fired: 2018-03-18 13:22:17 +0000
Fired: 2018-03-18 13:22:18 +0000
Timer fired: 2018-03-18 13:22:18 +0000
Fired: 2018-03-18 13:22:19 +0000
Fired: 2018-03-18 13:22:20 +0000
Timer fired: 2018-03-18 13:22:20 +0000
Fired: 2018-03-18 13:22:21 +0000
Fired: 2018-03-18 13:22:22 +0000
Fired: 2018-03-18 13:22:23 +0000
Fired: 2018-03-18 13:22:24 +0000
Fired: 2018-03-18 13:22:25 +0000
Fired: 2018-03-18 13:22:26 +0000
Fired: 2018-03-18 13:22:27 +0000
Fired: 2018-03-18 13:22:28 +0000
Fired: 2018-03-18 13:22:29 +0000
Fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Timer fired: 2018-03-18 13:22:30 +0000
Fired: 2018-03-18 13:22:31 +0000
Timer fired: 2018-03-18 13:22:31 +0000
Fired: 2018-03-18 13:22:32 +0000
Timer fired: 2018-03-18 13:22:32 +0000

Тот, который говорит "Уволен", является фактическим даже обработчиком DispatchSourceTimer в то время как "Timer Fired" находится в функции обработчика, которая обновляет метку и тому подобное. Ясно, что вы можете видеть, что таймер работает нормально, но кажется, что основной поток активно используется.

Вот мой код:

//timer is a DispatchSourceTimer, queue is a concurrent custom queue
public func startTimer() {
    timer?.cancel()
    timer = DispatchSource.makeTimerSource(queue: queue)
    timer?.schedule(deadline: DispatchTime.now(), repeating: .seconds(1))
    timer?.setEventHandler { [weak self] in
        print("Fired: \(Date())")
        DispatchQueue.main.async {
            self?.notifyObservers()
        }
    }
    timer?.resume()
}

public func stopTimer() {
    timer?.cancel()
    timer = nil
}

А вот уведомитель наблюдателей:

private func notifyObservers() {
    for observer: TimeObserver in self.timeObservers {
        observer.timerFired()
    }
}

Я поддерживаю массив объектов-наблюдателей, которые хотят получать уведомления о срабатывании таймера.

Я сделал тест и удалил цикл for и только что уведомил первого наблюдателя в списке, и это немного ускорило процесс, но все равно не решило проблему.

если я просто проигрываю песню и через несколько секунд основной поток на самом деле может справиться с этим, и я могу отлично показать время, но при выполнении тяжелых вещей, таких как использование MPMediaPlayer рамки для изменения песни или при звонке play() а также pause() на MPMusicPlayerController это начинает отставать.

Любая помощь будет оценена. Спасибо.

1 ответ

Вы можете увидеть это поведение, если вы создадите экземпляр своего игрока следующим образом:

let player = MPMusicPlayerController()

Но вы не увидите эту задержку (или, по крайней мере, намного меньше), если будете использовать:

let player = MPMusicPlayerController.applicationQueuePlayer

или же:

let player = MPMusicPlayerController.applicationMusicPlayer

На моем iPhone X я испытываю 5–8-секундную блокировку основного потока с помощью первого метода, но только на секунду или меньше с последними двумя методами.


Я предполагаю, что ваш таймер отправки должен был проверить, что происходит, но нет смысла использовать этот таймер отправки в фоновом потоке, если все, что он собирается сделать, это отправить его обратно в основную очередь. Если ваш основной поток заблокирован по независящим от вас причинам, вы не хотите создавать основной поток с кучей обновлений, которые не могли быть обработаны своевременно.

Я бы предложил вернуться к простому Timer,

Другие вопросы по тегам