添加AVPlayer周期性时间观察者后,类似YouTube布局的TableView卡顿
解决AVPlayer周期时间观察者导致TableView冻结的问题
你猜得完全正确!问题根源就是把时间观察者的回调队列指定为DispatchQueue.main了。主队列是负责所有UI渲染和用户交互的核心队列,你的回调每秒触发一次,哪怕只是简单的print操作,也会持续抢占主队列的资源,导致TableView的滚动、刷新这些UI操作得不到足够的执行时间,自然就出现冻结卡顿了。
具体解决方案
- 创建专属的后台串行队列
先给时间观察者单独建一个后台队列,用来处理非UI相关的逻辑,完全避开主队列的资源竞争:
// 自定义队列标签,方便调试时区分 let timeObserverQueue = DispatchQueue(label: "com.yourapp.avplayer.time.callback")
- 替换队列并拆分UI与非UI逻辑
把时间观察者的queue参数换成刚才创建的后台队列,如果后续需要更新UI(比如进度条、时间显示),再通过DispatchQueue.main.async切回主队列执行:
observer = self.avPlayer.addPeriodicTimeObserver( forInterval: CMTimeMake(1, 1), queue: timeObserverQueue, using: { [weak self] time in guard let self = self else { return } // 非UI操作在后台队列执行,不影响主队列 let currentSeconds = CMTimeGetSeconds(time) let totalDuration = self.avPlayer.currentItem?.asset.duration.seconds print(currentSeconds, totalDuration) // 若需更新UI,必须切回主队列 DispatchQueue.main.async { // 示例:更新进度条 // self.progressBar.progress = Float(currentSeconds / (totalDuration ?? 1)) } } )
额外的优化要点
- 防止内存泄漏:回调里一定要用
[weak self],避免AVPlayer和当前控制器形成强引用循环,导致对象无法正常销毁。 - 调整回调频率:如果业务不需要每秒更新一次,可以把时间间隔调大,比如
CMTimeMake(1, 2)就是每0.5秒触发一次,减少回调次数。 - 及时清理观察者:在控制器销毁或者AVPlayer不再使用时,记得移除时间观察者,避免无效回调占用资源:
if let timeObserver = observer { self.avPlayer.removeTimeObserver(timeObserver) observer = nil }
内容的提问来源于stack exchange,提问作者Deiby Toralva Baldeón
相关产品推荐
相关产品推荐

