34

I am creating a simple media player app. My App is crashed when first link is played and I clicked second link in 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> )'

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

Mihir Oza
  • 2,768
  • 3
  • 35
  • 61

5 Answers5

52

I had a similar problem. It worked fine in iOS 7, and now it crashes in 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) :

- (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];
}
Ralph
  • 716
  • 5
  • 9
  • I put this if(player!= nil) { if(player.currentItem != nil) { [player.currentItem removeObserver:self forKeyPath:@"status"]; } } instead of if (player != nil) [player removeObserver:self forKeyPath:@"status"]; and working great for me. – Mihir Oza Nov 21 '14 at 05:26
  • Hi guys, I am also stuck with this issue, was searching for a solution for last 2 days. Please help me to fix this. I have posted the question here - http://stackoverflow.com/questions/27838356/avplayeritem-was-deallocated-while-key-value-observers-were-still-registered-wit – NITHIN SHAHRUKH Jan 08 '15 at 14:36
  • 1
    i implement you code but get error Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer for the key path "status" from because it is not registered as an observer.' – Hardik Kardani Jan 09 '15 at 04:16
  • This give exception that "Cannot remove an observer for the key path "status" from because it is not registered as an observer." – Abuzar Amin Feb 26 '16 at 17:28
  • @AbuzarAmin use try catch for this. `@try { [player removeObserver:self forKeyPath:@"status"]; } @catch (NSException *exception) { } @finally { }` – Sushil Sharma Jul 08 '16 at 08:15
  • Thanks for such a simple solution @Ralph, I wasted my days searching for the solution – Sushil Sharma Jul 08 '16 at 08:17
3
-(void)viewWillDisappear:(BOOL)animated
{
[self.player removeObserver:self forKeyPath:@"status" context:nil];
}
  • Can you provide a little context around your solution? – wogsland Feb 16 '17 at 13:08
  • 1
    Whilst this code snippet is welcome, and may provide some help, it would be [greatly improved if it included an explanation](//meta.stackexchange.com/q/114762) of *how* and *why* this solves the problem. Remember that you are answering the question for readers in the future, not just the person asking now! Please [edit] your answer to add explanation, and give an indication of what limitations and assumptions apply. – Toby Speight Feb 16 '17 at 13:30
2

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

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"];
}
mmccomb
  • 13,516
  • 5
  • 39
  • 46
  • 1
    Application crashed. Crash Log :'Cannot remove an observer for the key path "timedMetadata" from because it is not registered as an observer.' Application crashed only in IOS 8 device. – Mihir Oza Oct 08 '14 at 12:57
  • 1
    mmcombe is correct, except the error refers to the AVPlayer's status' KVO. just change his answer to [[player]removeObserver:self forKeyPath:@"status"]; – MDB983 Oct 08 '14 at 14:37
  • My application is crash in IOS 8 and following is my crash log: "Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x15edf250 of class AVPlayerItem was deallocated while key value observers were still registered with it." – Mihir Oza Nov 11 '14 at 13:03
0

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

An instance 0x174034600 of class AVKeyPathFlattener was deallocated while key value observers were still registered with it. Current observation info: ( Context: 0x0, Property: 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.

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

0

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

@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");
}
neowinston
  • 7,584
  • 10
  • 52
  • 83