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

如何检查AVPlayer的keyPath观察者是否已添加并在存在时移除?

Checking and Removing KVO Observer for AVPlayer's currentItem.loadedTimeRanges

Great question! Unfortunately, Apple's KVO framework doesn't expose a direct API to check if an observer is already registered for a specific keyPath. But there are straightforward, reliable workarounds to handle this safely.

The Most Reliable Approach: Track Observer State Manually

The simplest and safest way is to maintain your own state flag to track whether the observer has been added. This avoids any guesswork and prevents duplicate additions/removals.

Step 1: Add a State Variable

Define a private boolean property in your class to track the observer's status:

private var isLoadedTimeRangesObserverActive = false

Step 2: Safely Add the Observer

When you want to add the observer, first check the state flag to avoid duplicates:

func setupAVPlayerObserver() {
    guard let player = audioPlayer, !isLoadedTimeRangesObserverActive else { return }
    
    player.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)
    isLoadedTimeRangesObserverActive = true
}

Step 3: Check and Remove the Observer

In any function where you need to remove the observer, check the flag first to ensure you only remove it if it's active:

func cleanupAVPlayerObserver() {
    guard let player = audioPlayer, isLoadedTimeRangesObserverActive else { return }
    
    player.removeObserver(self, forKeyPath: "currentItem.loadedTimeRanges")
    isLoadedTimeRangesObserverActive = false
}

Bonus: Use a Custom Context for Safer KVO

To avoid conflicts with other KVO observers in your class (or from superclasses), it's best practice to use a custom context pointer instead of nil:

Define a Custom Context

private static let loadedTimeRangesContext = UnsafeMutableRawPointer(bitPattern: 0x1001)!

Update Add/Remove Calls

// Adding the observer
player.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: Self.loadedTimeRangesContext)

// Removing the observer
player.removeObserver(self, forKeyPath: "currentItem.loadedTimeRanges", context: Self.loadedTimeRangesContext)

Update Your observeValue Method

Make sure to only handle the KVO update for your specific context:

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    // Delegate to superclass if this isn't our context
    guard context == Self.loadedTimeRangesContext else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        return
    }
    
    // Handle the loadedTimeRanges update here
    if let timeRanges = change?[.newKey] as? [NSValue] {
        // Process the loaded time ranges
    }
}

Critical Reminder: Clean Up in deinit

Always remember to remove the observer in your class's deinitializer to prevent memory leaks or crashes:

deinit {
    cleanupAVPlayerObserver()
}

What About Just Calling removeObserver Directly?

On iOS 9 and later, calling removeObserver(_:forKeyPath:) for an observer that doesn't exist won't crash (unlike earlier iOS versions). However, relying on this is still risky—you might accidentally remove an observer you didn't mean to, or miss cleanup if your state isn't tracked. Manual state tracking is always the cleaner, more maintainable approach.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:08:51