Лаги в пользовательском интерфейсе при использовании таймера
Я использую таймер, повторяющийся каждую секунду, чтобы обновить проигрывание 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
,