Swift中AVPlayer准备播放时KVO观察者未触发问题排查
解决AVPlayer KVO监听
playbackLikelyToKeepUp无回调的问题 你遇到的observeValue从未触发的问题,大概率是链式KVO监听的逻辑漏洞,或是AVPlayer/AVPlayerItem的配置问题导致的。我帮你拆解几个核心排查点和解决方法:
1. 链式KVO路径的陷阱:currentItem为nil时无法监听子属性
你直接监听"currentItem.playbackLikelyToKeepUp",但如果一开始player.currentItem是nil,KVO不会自动追踪后续currentItem的子属性变化。正确的做法是先监听currentItem的存在状态,再给非nil的currentItem添加子属性监听:
// 定义两个独立的context区分不同监听 var currentItemContext = 0 var playbackLikelyToKeepUpContext = 0 // 先监听currentItem的变化 if let video = card.pageImageVideoController.controllers[0] as? VideoController { video.player.addObserver(self, forKeyPath: "currentItem", options: [.new, .old], context: ¤tItemContext) } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard let videoController = topCard?.pageImageVideoController.controllers[0] as? VideoController else { return } if context == ¤tItemContext { // 移除旧currentItem的监听(防止内存泄漏) if let oldItem = change?[.oldKey] as? AVPlayerItem { oldItem.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp", context: &playbackLikelyToKeepUpContext) } // 给新的currentItem添加playback状态监听 if let newItem = change?[.newKey] as? AVPlayerItem { newItem.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .new, context: &playbackLikelyToKeepUpContext) } } else if context == &playbackLikelyToKeepUpContext { if let item = object as? AVPlayerItem, item.isPlaybackLikelyToKeepUp { print("ready") } else { print("not ready") } } else { // 务必调用父类实现,处理其他未匹配的KVO super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) } }
2. 先确认AVPlayerItem是否被正确赋值
如果player.currentItem始终是nil,任何监听都不会触发。在添加KVO前加个打印排查:
if let video = card.pageImageVideoController.controllers[0] as? VideoController { print("Current AVPlayerItem: \(video.player.currentItem)") // 后续监听代码 }
如果输出为nil,检查是否调用过player.replaceCurrentItem(with: yourAVPlayerItem)来关联视频资源。
3. 确保观察者对象未被提前释放
如果你的self(观察者)被提前销毁(比如ViewController被pop但未移除监听),observeValue自然不会触发。同时务必在对象销毁前移除所有KVO监听,避免崩溃:
deinit { if let video = card.pageImageVideoController.controllers[0] as? VideoController { video.player.removeObserver(self, forKeyPath: "currentItem", context: ¤tItemContext) if let item = video.player.currentItem { item.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp", context: &playbackLikelyToKeepUpContext) } } }
额外推荐:用Combine替代KVO(更简洁安全)
现在AVFoundation推荐用Combine框架监听状态,避免KVO的繁琐和潜在问题:
import Combine var cancellables = Set<AnyCancellable>() if let video = card.pageImageVideoController.controllers[0] as? VideoController { video.player.publisher(for: \.currentItem) .switchToLatest { item in // 若currentItem为nil,默认返回false item?.publisher(for: \.isPlaybackLikelyToKeepUp) ?? Just(false).eraseToAnyPublisher() } .sink { isReady in isReady ? print("ready") : print("not ready") } .store(in: &cancellables) }
内容的提问来源于stack exchange,提问作者StackGU
相关产品推荐
相关产品推荐

