34

我正在创建一个简单的媒体播放器应用程序。播放第一个链接并单击 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");
        }
    }
}

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

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

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

4

5 回答 5

52

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

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

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

-(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];
}

同样在 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];
}
于 2014-11-11T16:37:22.673 回答
3
-(void)viewWillDisappear:(BOOL)animated
{
[self.player removeObserver:self forKeyPath:@"status" context:nil];
}
于 2017-02-16T12:39:56.970 回答
2

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

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

- (IBAction)btnStop_Click:(id)sender {
    [[player currentItem] removeObserver:self forKeyPath:@"timedMetadata"];
}
于 2014-10-08T12:45:04.207 回答
0

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

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

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

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

于 2018-02-02T03:34:34.497 回答
0

明智的做法是先验证是否正在观察密钥,然后再使用 删除观察者@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");
}
于 2019-02-18T20:51:29.003 回答