调用removeObserver无法移除AVPlayer.currentItem绑定的观察者求助
问题解决:AVPlayer循环观察者无法移除的修复方案
核心问题原因
你使用的闭包形式addObserver(forName:object:queue:using:)会返回一个独立的观察者对象,但你调用removeObserver(self, ...)是错误的——这个观察者并非self,而是方法返回的NSObjectProtocol实例。没保存这个实例的引用,就没法精准移除旧观察者。
修复步骤
新增观察者引用存储
添加一个@State变量,用来保存每个AVPlayerItem对应的观察者,确保能定位到要移除的对象:@State var players: [AVPlayer] = [myAVPlayers] @State var selectedIndex: Int = 0 // 存储AVPlayerItem到观察者的映射关系 @State var playerObservers: [AVPlayerItem: NSObjectProtocol] = [:]切换视频时先清理旧观察者
在更新selectedIndex之前,先处理上一个视频的观察者:DragGesture() .onEnded { _ in // 1. 获取上一个视频的AVPlayerItem let previousPlayerItem = players[selectedIndex].currentItem // 2. 取出对应观察者并移除 if let previousObserver = playerObservers[previousPlayerItem] { NotificationCenter.default.removeObserver(previousObserver) playerObservers.removeValue(forKey: previousPlayerItem) } // 3. 更新selectedIndex到新值(此处需根据你的滑动逻辑替换为实际的newIndex) selectedIndex = newIndex // 4. 为新视频添加循环观察者 let currentPlayerItem = players[selectedIndex].currentItem guard let item = currentPlayerItem else { return } let observer = NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: item, queue: .main) { [weak self] _ in guard let self = self else { return } // 修正原代码错误:players[selectedIndex]是AVPlayer,直接调用seek和play self.players[self.selectedIndex].seek(to: .zero) self.players[self.selectedIndex].play() } // 5. 将新观察者存入映射表 playerObservers[item] = observer }页面销毁时清理所有观察者
避免内存泄漏,在View销毁时移除剩余观察者:.onDisappear { for observer in playerObservers.values { NotificationCenter.default.removeObserver(observer) } playerObservers.removeAll() }
关键注意事项
- 闭包形式添加的观察者,必须用返回的实例调用
removeObserver(_:),不能用removeObserver(self)。 - 闭包内使用
[weak self]避免循环引用,防止内存泄漏。 - 每次添加新观察者前,务必先移除旧的,避免重复注册导致多次触发回调。
内容的提问来源于stack exchange,提问作者nickcoding2
相关产品推荐
相关产品推荐

