AVPlayer был освобожден, а наблюдатели с ключевыми значениями все еще были зарегистрированы с ним

Я создаю простое приложение для медиаплеера. Мое приложение разбилось, когда была сыграна первая ссылка, и я нажал вторую ссылку в 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", причина: "Экземпляр 0x165297c0 класс AVPlayer был освобожден, в то время как наблюдатели ключевых значений все еще были зарегистрированный им. Текущая информация о наблюдении:  (Контекст: 0x0, Свойство: 0x1661d5d0 > ) '

Приложение, разбитое только в IOS 8 в IOS 7, отлично работает. Что я делаю неправильно?

Ответ 1

У меня была аналогичная проблема. Он отлично работал в 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];
}

Ответ 2

При использовании KVO вы должны балансировать вызовы addObserver:forKeyPath:options:context: с вызовами removeObserver:forKeyPath: (см. руководство по программированию KVO).

Попробуйте удалить контроллер вида в качестве наблюдателя, когда кнопка остановки нажата, например.

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

Ответ 3

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

Ответ 4

Я столкнулся с подобной проблемой при использовании AVPlayer, информация о сбое журнала сообщает:

Экземпляр 0x174034600 класса AVKeyPathFlattener был освобожден, а наблюдатели с ключевыми значениями все еще были зарегистрированы с ним. Текущая информация наблюдений: (Контекст: 0x0, Свойство: 0x17405d6d0 > )

Как и рекомендовал Apple, я изначально сделал добавление наблюдателя после инициализации объекта AVPlayerItem и удаления наблюдателя в методе dealloc наблюдателя. Поскольку мой класс наблюдателя содержал большую ссылку на мой объект AVPlayerItem, поэтому его нельзя освобождать до того, как объект моего наблюдателя будет освобожден. Я действительно не знаю, почему это происходит.

Итак, я попытался решить эту проблему, используя BlocksKit, теперь это отлично работает.