web-dev-qa-db-fra.com

AVPlayer a été désalloué alors que des observateurs clés étaient encore enregistrés avec lui

Je crée une application de lecteur multimédia simple. Mon application est bloquée lorsque le premier lien est joué et que j'ai cliqué sur le deuxième lien dans 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");
        }
    }
}

J'ai reçu ce message quand App s'est écrasé.

*** Application terminée en raison d'une exception non interceptée 'NSInternalInconsistencyException', raison: 'Une instance 0x165297c0 de La classe AVPlayer a été désallouée alors que les observateurs clés étaient encore enregistré avec elle. Informations d'observation actuelles: (Contexte: 0x0, Propriété: 0x1661d5d0>) '

L'application s'est écrasée uniquement dans IOS 8 dans IOS 7 fonctionne correctement. Qu'est ce que je fais mal??

29
Mihir Oza

J'avais un problème similaire. Cela fonctionnait bien sous iOS 7 et maintenant, il se bloque sous iOS 8.

La solution consistait à supprimer l'observateur avant de relâcher l'objet.

Lorsque vous remplacez ou allouez un nouvel objet à un membre, vous libérez l'ancien objet. Vous devez donc d'abord supprimer l'observateur:

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

Et de même dans btnPlayClick (au cas où il est appuyé sans appuyer sur 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];
}
49
Ralph
-(void)viewWillDisappear:(BOOL)animated
{
[self.player removeObserver:self forKeyPath:@"status" context:nil];
}
3
Mohit Singh

Lorsque vous utilisez KVO, vous devez équilibrer les appels vers addObserver:forKeyPath:options:context: avec les appels vers removeObserver:forKeyPath: (voir le guide de programmation KVO ).

Essayez de supprimer le contrôleur de vue en tant qu'observateur lorsque vous appuyez sur le bouton d'arrêt, par exemple.

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

Il est sage de vérifier d'abord si la clé est observée ou non avant de retirer l'observateur avec un @try @catch, comme ceci:

@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");
}
0
Winston

J'ai rencontré le même problème en utilisant AVPlayer, les informations du journal des incidents indiquent:

Une instance 0x174034600 de la classe AVKeyPathFlattener a été désallouée alors que des observateurs de valeur de clé étaient toujours enregistrés avec elle. Informations d'observation actuelles: (Contexte: 0x0, Propriété: 0x17405d6d0>)

Comme recommandé par Apple, j'ai à l'origine ajouté l'ajout d'un observateur après l'initialisation de mon objet AVPlayerItem et la suppression de l'observateur dans la méthode dealloc de l'observateur. Parce que ma classe d'observateur a gardé une référence forte sur mon objet AVPlayerItem, il ne doit donc pas être désalloué avant que mon objet observateur ait été désalloué. Je ne sais vraiment pas pourquoi cela se produit.

J'ai donc essayé de résoudre ce problème en utilisant BlocksKit , cela fonctionne bien pour moi maintenant.

0
ShannonChenCHN