AVPlayer播放结束通知非注册线程触发时如何保障UI操作线程安全
解决方案
线程保障的两种实现方式
两种方案都可以实现弹窗在主线程执行,优先推荐第二种带队列参数的监听方式:
- 方案1:保留现有selector监听方式,在
presentAlert方法内部手动派发至主队列执行
代码示例:@objc func presentAlert() { DispatchQueue.main.async { // 此处写弹窗创建、展示的代码 let alert = UIAlertController(title: "播放完成", message: nil, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) self.present(alert, animated: true) } }注意:所有UI相关操作必须放在主队列的闭包内,不能漏写。
- 方案2:改用
addObserver(forName:object:queue:using:)方法,直接传入主队列,回调默认在主线程执行
代码示例:// 将observer引用存为类属性,后续销毁观察者时需要用到 private var playEndObserver: NSObjectProtocol? // 监听代码 playEndObserver = NotificationCenter.default.addObserver( forName: .AVPlayerItemDidPlayToEndTime, object: currentPlayItem, // 这里要填当前正在播放的AVPlayerItem实例,不要传nil queue: OperationQueue.main, using: { [weak self] _ in guard let self = self else { return } // 直接编写弹窗代码,已经运行在主线程 let alert = UIAlertController(title: "播放完成", message: nil, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) self.present(alert, animated: true) } )
额外修复弹窗触发时机异常的问题
你遇到的重播中途触发、其他页面弹出的问题,除了线程因素外,还有两处明显的配置错误需要修正:
- 现有监听代码的
object参数传了nil,会接收全局所有AVPlayerItem的播放结束通知,包括其他页面的播放实例、你之前播放过的旧资源的通知,必须把这个参数设置为当前你正在监听的AVPlayerItem实例。 - 必须在页面销毁、切换播放资源的时候移除对应观察者:
- 如果用selector方式:
NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: currentPlayItem) - 如果用block方式:
if let observer = playEndObserver { NotificationCenter.default.removeObserver(observer) }
- 如果用selector方式:
内容的提问来源于stack exchange,提问作者Curiosity
相关产品推荐
相关产品推荐

