シンプルなメディアプレーヤーアプリを作成しています。最初のリンクが再生され、uitableviewで2番目のリンクをクリックすると、アプリがクラッシュします。
- (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 8 in IOS 7は正常に動作します。でクラッシュしました。
同様の問題がありました。 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];
}
-(void)viewWillDisappear:(BOOL)animated
{
[self.player removeObserver:self forKeyPath:@"status" context:nil];
}
KVOを使用するときは、addObserver:forKeyPath:options:context:
呼び出しremoveObserver:forKeyPath:
( KVOプログラミングガイド を参照)。
停止ボタンがタップされたときに、オブザーバーとしてView Controllerを削除してみてください。
- (IBAction)btnStop_Click:(id)sender {
[[player currentItem] removeObserver:self forKeyPath:@"timedMetadata"];
}
次のように、@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");
}
AVPlayerを使用したときに同様の問題に遭遇しました。クラッシュログ情報には次のように記載されています。
クラスKeyKeyPathFlattenerのインスタンス0x174034600は、キー値オブザーバーがまだ登録されている間に割り当て解除されました。現在の観測情報:(コンテキスト:0x0、プロパティ:0x17405d6d0>)
Appleが推奨されているように、私が最初にしたことは、AVPlayerItemオブジェクトを初期化した後にオブザーバーを追加し、オブザーバーのdeallocメソッドでオブザーバーを削除することです。オブザーバオブジェクトの割り当てが解除される前に割り当てを解除すべきではありません。
そこで、 BlocksKit
を使用してこの問題を解決しようとしましたが、今のところうまくいきます。