You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AVPlayer的addPeriodicTimeObserver函数执行异常及设计咨询

问题解答

1. 为什么闭包内添加简单语句就会导致AVPlayer无法播放、应用卡死?

核心原因是触发了无限循环,完全占用了主线程资源,具体逻辑链如下:

  • 当你在addPeriodicTimeObserver的闭包里更新self.doubleValue时,会触发PlayerSlider中doubleValue的didSet方法
  • 在didSet里,你调用了mediaManager.player?.seek(to:),这个操作会立即修改AVPlayer的当前播放时间
  • AVPlayer的时间变化又会触发addPeriodicTimeObserver的闭包再次执行,再次更新doubleValue,形成无限循环
  • 这个循环在主线程持续执行,会彻底阻塞主线程,导致应用卡死,音频播放的逻辑自然也无法正常运行

另外还有几个加剧问题的细节:

  • 代码中大量使用强制解包(比如mediaManager.player!、currentItem!.duration),如果player或currentItem为nil,会直接崩溃,还隐藏了潜在的空值风险
  • 在isEnabled的didSet里重复注册通知,每次isEnabled变化都会添加新的观察者,导致setupPlayerObserver被多次调用,添加多个时间观察者,进一步加重主线程负担

2. 这种单例+Slider监听的设计是否合理?

整体设计存在不少耦合和资源管理的问题,不算合理,具体问题和改进建议如下:

存在的问题

  • 耦合度过高:PlayerSlider作为UI控件,直接持有MediaManager单例,违反了单一职责原则——UI控件只应该负责UI的展示和交互,不应该直接依赖业务逻辑层的单例,这会导致控件难以复用,也不利于后续的测试和维护
  • 单例的副作用:MediaManager单例的生命周期和应用绑定,容易导致内存泄漏(比如这里Slider和单例之间的隐式强引用循环),而且单例的全局访问会让状态变化难以追踪
  • 资源管理混乱:切换音频时,旧的AVPlayer和时间观察者没有被移除,会造成内存泄漏;通知没有在合适的时机移除,会导致重复触发
  • 状态逻辑不清晰:currentFile的didSet直接触发播放,没有处理暂停、旧资源释放等边界情况

改进建议

  • 用依赖注入替代单例直接引用:给PlayerSlider添加初始化参数,传入MediaManager的实例(或者定义一个播放控制协议,让MediaManager遵守,降低耦合),这样控件不依赖全局单例,更灵活
  • 分离UI和业务逻辑:让PlayerSlider只负责接收进度更新的回调,以及向外部发送用户拖动滑块的seek请求,不直接操作AVPlayer
  • 正确管理观察者资源:在PlayerSlider的deinit方法中移除通知观察者和AVPlayer的时间观察者;在MediaManager切换音频时,先停止旧的player,移除旧的观察者,再创建新的player
  • 优化状态逻辑:在currentFile的didSet中,先判断是否需要停止当前播放,再初始化新的player,避免资源浪费

内容的提问来源于stack exchange,提问作者Sam Fischer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:18:00