iOS An instance of AVPlayer cannot remove a time observer that was added by a different instance

0x00 进退两难

在利用AVPlayer播放网络 mp3 时,因为有监听状态,所以移除监听是必须的
不然会出现以下错误:
An instance 0x174016100 of class AVPlayerItem was deallocated while key value observers were still registered with it.

不移除观察者时,导致的崩溃:

Trapped uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x174016100 of class AVPlayerItem was deallocated while key value observers were still registered with it. Current observation info:  (
 Context: 0x0, Property: 0x174848d00>
 Context: 0x0, Property: 0x174841f20>
 Context: 0x0, Property: 0x174849ff0>
)'

然而,有个蛋疼的操作出现了
当移除观察者时
另外一个Bug出现了
Trapped uncaught exception 'NSInvalidArgumentException', reason: 'An instance of AVPlayer cannot remove a time observer that was added by a different instance of AVPlayer.'

这移除不行!
这不移除也不行!
这要怎么玩??


0x01 情况分析

1.播放音频
1.1移除前一个音频的监听状态(如果存在)
1.2添加新的监听状态

2.状态监听回调方法
2.1成功则播放
2.2失败
2.2.1第1次失败,尝试再次播放 -> 走步骤1
2.2.2第2次失败,移除监听状态

就是在步骤 2.2.2,出现了进退两难的情况


0x02 解决方案

既然移除是必须的
那就只能走移除!

但是移除又会导致:
Trapped uncaught exception 'NSInvalidArgumentException', reason: 'An instance of AVPlayer cannot remove a time observer that was added by a different instance of AVPlayer.'

只能前进
如何后退呢?

如果你能想到的话
你已经知道我要说什么了

如果你还没想到
继续往下看

iOS中有个捕获异常,防止崩溃的机制:@try @catch

@try {
	// 可能存在异常
	[self.player removeTimeObserver:self.timeObserver];
} @catch (NSException *exception) {
	// 查看异常
	NSLog(@"RemoveObserver:%@",exception);
} @finally {
	// 去掉 finally 也可以
}

虽然这个捕获异常的机制
捕获的异常有限
但这里确实能防止应用崩溃
Nice!


0x03 后续

2020-07-28 18:04:36

在移除监听时,应该判断timeObserver是否存在
并在移除后置空

- (void)removeObserver
{
    if (self.player.currentItem) {
        @try {
            [self.player.currentItem removeObserver:self forKeyPath:@"status"];
            [self.player.currentItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
            [self.player.currentItem removeObserver:self forKeyPath:@"playbackBufferEmpty"];
            
            [self.player replaceCurrentItemWithPlayerItem:nil];
            
            if (self.timeObserver) {
                [self.player removeTimeObserver:self.timeObserver];
                self.timeObserver = nil;
            }
            
        } @catch (NSException *exception) {
            NSLog(@"removeObserver:%@",exception);
        } @finally {
            
        }
    }
}

就是因为前面没置空
所以导致后面移除时
AVPlayer匹配不上


0x04 继续

2020-07-28 18:09:13

遇到一个加载 mp3 url 后
无法播放的Bug:
Error Domain=AVFoundationErrorDomain Code=-11819 "无法完成操作" UserInfo={NSLocalizedDescription=无法完成操作, NSLocalizedRecoverySuggestion=请稍后再试。}

接着,所以其他的 mp3 url 资源都无法播放了
所有报错都是这样:
Error Domain=AVFoundationErrorDomain Code=-11819 "无法完成操作" UserInfo={NSLocalizedDescription=无法完成操作, NSLocalizedRecoverySuggestion=请稍后再试。, NSURL=https://xxx/xxx/xxx.mp3, NSUnderlyingError=0x174a51670 {Error Domain=NSOSStatusErrorDomain Code=268435459 "(null)"}}

代码使用了同一个AVPlayer实例
猜测原因是
AVPlayer内部有缓存
导致后面加载的 url 都不请求了

最终尝试如下操作
解决了这个蛋疼的问题

在回调方法里
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
针对播放失败状态所做的处理

if ([keyPath isEqualToString:@"status"]) {
    switch ([[change valueForKey:@"new"] integerValue]) {
        case AVPlayerItemStatusFailed: {
		    NSError * error = _player.currentItem.error;
		    if (error) {
                    NSInteger code = erro.code;
                    NSInteger count = erro.userInfo.count;
                    
                    if (code == -11819 &&
                        count == 2) {
                        
                        [self removeObserver];
                        _player = nil;
                    }
            }
            // 可以尝试重新加载
            // 或者其他操作
        }
        break;
	}
}

0x05 再续

有些音频即使再次加载
也还是不能播放
错误有2种:

第一种:-11800,AVErrorUnknown,未知错误 T_T

Error Domain=AVFoundationErrorDomain Code=-11800 "这项操作无法完成" UserInfo={NSLocalizedFailureReason=发生未知错误(-16041), NSLocalizedDescription=这项操作无法完成, NSUnderlyingError=0x2801d9a70 {Error Domain=NSOSStatusErrorDomain Code=-16041 "(null)"}}

第二种:byte range length mismatch T_T

Error Domain=NSURLErrorDomain Code=-1 "未知错误" UserInfo={NSLocalizedDescription=未知错误, NSUnderlyingError=0x283db2be0 {Error Domain=CoreMediaErrorDomain Code=-12939 "byte range length mismatch - should be length 16384 is length 24137" UserInfo={NSDescription=byte range length mismatch - should be length 16384 is length 24137, NSURL=http://xxx/xxx/xxx.mp3}}}

既然系统的下载不行
那就尝试手动下载
由于音频不是很大
所以采取先下载后播放的策略
最终解决了这2个蛋疼的bug!


iOS 验证码输入框

https://github.com/xjh093/JHVerificationCodeView


你可能感兴趣的:(iOS各种报错)