1

現在再生中の を更新するために毎秒繰り返すタイマーを使用しているcurrentPlaybackTimeため、現在の曲の進行状況で毎秒ラベルを更新しています。NSTimer以前は Swift で orを使用してTimer1 秒ごとに繰り返していましたが、曲を変更したり、曲を開始および停止したりするときに問題が発生しました。再生してから、正しい時間に更新されます)。その後、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

「Fired」と表示されているのは実際の偶数ハンドラーですが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が始まります。

どんな助けでも大歓迎です。ありがとうございました。

4

1 に答える 1

0

プレーヤーを次のようにインスタンス化すると、この動作を確認できます。

let player = MPMusicPlayerController()

ただし、次を使用すると、この遅延は発生しません (または、少なくともはるかに短くなります)。

let player = MPMusicPlayerController.applicationQueuePlayer

また:

let player = MPMusicPlayerController.applicationMusicPlayer

私の iPhone X では、前者の手法では 5 ~ 8 秒のメイン スレッドのブロックが発生しますが、後者の 2 つの手法では 1 秒以下です。


あなたのディスパッチタイマーは何が起こっているかをテストするためのものだったと思いますが、バックグラウンドスレッドでこのディスパッチタイマーを使用しても、メインキューにディスパッチするだけなら意味がありません。制御できない理由でメインスレッドがブロックされている場合、タイムリーに処理できなかった一連の更新でメインスレッドをバックログしたくありません。

シンプルに戻すことをお勧めしTimerます。

于 2018-03-18T15:56:43.453 に答える