ios AVPlayer 已被释放,而键值观察者仍在向其注册

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26256982/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 03:04:56  来源:igfitidea点击:

AVPlayer was deallocated while key value observers were still registered with it

iosobjective-cavplayeravplayeritem

提问by Mihir Oza

I am creating a simple media player app. My App is crashed when first link is played and I clicked second link in uitableview.

我正在创建一个简单的媒体播放器应用程序。我的应用程序在播放第一个链接时崩溃,我在 uitableview 中单击了第二个链接。

- (void)viewDidLoad {
        [super viewDidLoad];
        arrURL = [NSArray arrayWithObjects: @"http://yp.shoutcast.com/sbin/tunein-station.pls?id=148820", @"http://www.kcrw.com/pls/kcrwmusic.pls",@"http://yp.shoutcast.com/sbin/tunein-station.pls?id=175821",@"http://yp.shoutcast.com/sbin/tunein-station.pls?id=148820",@"http://yp.shoutcast.com/sbin/tunein-station.pls?id=70931",nil];
        url = [[NSURL alloc] init];    
    }

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

        return [arrURL count];
    }

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *MyIdentifier = @"MyIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
    if (cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] ;
    }
     cell.textLabel.text = [arrURL objectAtIndex:indexPath.row];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    selectedSongIndex = indexPath.row;
    url = [[NSURL alloc] initWithString:[arrURL objectAtIndex:indexPath.row]];
    [self setupAVPlayerForURL:url];
    [player play];

    //[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (IBAction)btnPlay_Click:(id)sender {

    [player play];
    AVPlayerItem *item = player.currentItem;
    [item addObserver:self forKeyPath:@"timedMetadata" options:NSKeyValueObservingOptionInitial| NSKeyValueObservingOptionNew| NSKeyValueObservingOptionOld| NSKeyValueObservingOptionPrior context:nil];
}
- (IBAction)btnPause_Click:(id)sender {

    [player pause];
}

- (IBAction)btnStop_Click:(id)sender {

    [player pause];
}
-(void) setupAVPlayerForURL: (NSURL*) url1 {
    AVAsset *asset = [AVURLAsset URLAssetWithURL:url1 options:nil];
    AVPlayerItem *anItem = [AVPlayerItem playerItemWithAsset:asset];

    player = [AVPlayer playerWithPlayerItem:anItem]; **//Application Crashed**
    [player addObserver:self forKeyPath:@"status" options:0 context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if([keyPath isEqualToString:@"timedMetadata"])
    {
        AVPlayerItem *item = (AVPlayerItem *)object;
        NSLog(@"Item.timedMetadata: %@",item.timedMetadata);
        NSLog(@"-- META DATA ---");
        //        AVPlayerItem *pItem = (AVPlayerItem *)object;
        for (AVMetadataItem *metaItem in item.timedMetadata) {
            NSLog(@"meta data = %@",[metaItem commonKey]);
            NSString *key = [metaItem commonKey]; //key = publisher , key = title
            NSString *value = [metaItem stringValue];
            NSLog(@"key = %@, value = %@", key, value);
            if([[metaItem commonKey] isEqualToString:@"title"])
            {
                self.lblTitle.text = [metaItem stringValue];
            }
        }
    }
    if (object == player && [keyPath isEqualToString:@"status"]) {
        if (player.status == AVPlayerStatusFailed) {
            NSLog(@"AVPlayer Failed");
        } else if (player.status == AVPlayerStatusReadyToPlay) {
            NSLog(@"AVPlayer Ready to Play");
        } else if (player.status == AVPlayerItemStatusUnknown) {
            NSLog(@"AVPlayer Unknown");
        }
    }
}

I got this message when App crashed.

当应用程序崩溃时,我收到了这条消息。

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x165297c0 of class AVPlayer was deallocated while key value observers were still registered with it. Current observation info: ( Context: 0x0, Property: 0x1661d5d0> )'

*** 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“类 AVPlayer 的实例 0x165297c0 已被释放,而键值观察者仍在其中注册。当前观察信息:(上下文:0x0,属性:0x1661d5d0>)'

Application crashed only in IOS 8 in IOS 7 works fine.What I am doing wrong??

仅在 IOS 8 中崩溃的应用程序在 IOS 7 中工作正常。我做错了什么??

回答by Ralph

I had a similar problem. It worked fine in iOS 7, and now it crashes in iOS 8.

我有一个类似的问题。它在 iOS 7 中运行良好,现在它在 iOS 8 中崩溃了。

The solution was to remove the observer, before releasing the object.

解决方案是在释放对象之前移除观察者。

When you replace or allocate a new object for a member, you're releasing the old object, so you need to remove the observer first :

当您为成员替换或分配新对象时,您正在释放旧对象,因此您需要先删除观察者:

-(void) setupAVPlayerForURL: (NSURL*) url1 {
    AVAsset *asset = [AVURLAsset URLAssetWithURL:url1 options:nil];
    AVPlayerItem *anItem = [AVPlayerItem playerItemWithAsset:asset];
    if (player != nil)
        [player removeObserver:self forKeyPath:@"status"];
    player = [AVPlayer playerWithPlayerItem:anItem]; 
    [player addObserver:self forKeyPath:@"status" options:0 context:nil];
}

And similarly in btnPlayClick ( in case it is pressed without btnStop_Click being pressed) :

同样在 btnPlayClick 中(以防在没有按下 btnStop_Click 的情况下按下它):

- (IBAction)btnPlay_Click:(id)sender {
     if (player != nil && [player currentItem] != nil)
         [[player currentItem] removeObserver:self forKeyPath:@"timedMetadata"];
    AVPlayerItem *item = player.currentItem;
    [item addObserver:self forKeyPath:@"timedMetadata" options:NSKeyValueObservingOptionInitial|     NSKeyValueObservingOptionNew| NSKeyValueObservingOptionOld| NSKeyValueObservingOptionPrior context:nil];
    [player play];
}

回答by Mohit Singh

-(void)viewWillDisappear:(BOOL)animated
{
[self.player removeObserver:self forKeyPath:@"status" context:nil];
}

回答by mmccomb

When using KVO you must balance calls to addObserver:forKeyPath:options:context:with calls to removeObserver:forKeyPath:(see the KVO programming guide).

使用 KVO 时,您必须平衡调用 toaddObserver:forKeyPath:options:context:和调用 to removeObserver:forKeyPath:(请参阅KVO 编程指南)。

Try removing the view controller as an observer when the stop button is tapped e.g.

当点击停止按钮时,尝试将视图控制器作为观察者移除,例如

- (IBAction)btnStop_Click:(id)sender {
    [[player currentItem] removeObserver:self forKeyPath:@"timedMetadata"];
}

回答by ShannonChenCHN

I did meet the similar issue when using AVPlayer, the crash log info says:

我在使用 AVPlayer 时确实遇到了类似的问题,崩溃日志信息说:

An instance 0x174034600 of class AVKeyPathFlattener was deallocated while key value observers were still registered with it. Current observation info: ( Context: 0x0, Property: 0x17405d6d0> )

AVKeyPathFlattener 类的实例 0x174034600 已被释放,而键值观察者仍在向其注册。当前观察信息:(上下文:0x0,属性:0x17405d6d0>)

As what Apple recommended, what I originally did is adding observer after initialize my AVPlayerItem object, and remove observer in the observer's dealloc method. Because my observer class kept a strong reference on my AVPlayerItem object, so it should not be deallocated before my observer object was deallocated. I really don't know why this happens.

正如苹果推荐的那样,我最初做的是在初始化我的 AVPlayerItem 对象后添加观察者,并在观察者的 dealloc 方法中删除观察者。因为我的观察者类在我的 AVPlayerItem 对象上保持了一个强引用,所以在我的观察者对象被释放之前它不应该被释放。我真的不知道为什么会发生这种情况。

So I tried solved this problem by using BlocksKit, it works fine for me right now.

所以我尝试通过使用来解决这个问题BlocksKit,它现在对我来说很好用。

回答by neowinston

It's wise to verify first if the key is being observed or not before removing the observer with a @try @catch, like so:

在使用 a 删除观察者之前,首先验证是否正在观察密钥是明智的@try @catch,如下所示:

@try {
    [self.player removeObserver:self forKeyPath:@"status" context:nil];
} @catch (id anException) {
    //do nothing, obviously it wasn't attached because an exception was thrown
    NSLog(@"status key not being observed");
}