编辑:罪魁祸首是iOS 8,而不是模拟器(我没有意识到已经在运行iOS 8),我已经重命名了标题来反映这一点.
我很高兴地使用this SO question的代码从mp3文件加载专辑封面.这是在我的iPhone 5与iOS 7.1.
但是,我跟踪了在iOS模拟器中崩溃的代码.进一步的调查显示,这个代码也在我的iPad上坠毁.它升级到iOS 8后,在我的iPad上坠毁.
看来包含图像的字典已损坏.
我创建了一个虚拟的iOS项目,只加载专辑封面,得到相同的结果.以下是该viewcontroller的代码.
- (void) viewDidAppear:(BOOL)animated { self.titleText = @"Overkill"; // Set song filename here NSString *filePath = [[NSBundle mainBundle] pathForResource:self.titleText ofType:@"mp3"]; if (!filePath) { return; } NSURL *fileURL = [NSURL fileURLWithPath:filePath]; NSLog(@"Getting song Metadata for %@",self.titleText); AVAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil]; if (asset != nil) { NSArray *keys = [NSArray arrayWithObjects:@"commonMetadata",nil]; [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{ NSArray *artworks = [AVMetadataItem MetadataItemsFromArray:asset.commonMetadata withKey:AVMetadataCommonKeyArtwork keySpace:AVMetadataKeySpaceCommon]; UIImage *albumArtWork; for (AVMetadataItem *item in artworks) { if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) { NSDictionary *dict = [item.value copyWithZone:nil]; // ********** // Crashes here with SIGABRT. dict is not a valid dictionary. // ********** if ([dict objectForKey:@"data"]) { albumArtWork = [UIImage imageWithData:[dict objectForKey:@"data"]]; } } else if ([item.keySpace isEqualToString:AVMetadataKeySpaceiTunes]) { // This doesn't appear to get called for images set (ironically) in iTunes albumArtWork = [UIImage imageWithData:[item.value copyWithZone:nil]]; } } if (albumArtWork != nil) { dispatch_sync(dispatch_get_main_queue(),^{ [self.albumArtImageView setImage:albumArtWork]; }); } }]; } }
我已经标记了崩溃的线.它期望一个文件Overkill.mp3在包中.我测试了多个mp3和m4a从iTunes和Amazon导出,所以我知道文件本身是正确的编码.
在Xcode 6.0和6.1中测试.
任何想法为什么它可以在iPhone上工作,而不是模拟器或iPad?
编辑/更新:
记录item.value显示差异.
在iPhone 5(作品):
(lldb) po item.value { MIME = JPG; data = <ffd8ffe0 .... several Kb of data ..... 2a17ffd9>; identifier = ""; picturetype = Other; }
在模拟器(崩溃)
(lldb) po item.value <ffd8ffe0 .... several Kb of data ..... 2a17ffd9>
所以似乎在模拟器上没有字典,只是原始的作品.
将代码更改为不期望字典,但将item.value作为UIImage工作!
for (AVMetadataItem *item in artworks) { if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) { NSData *newImage = [item.value copyWithZone:nil]; albumArtWork = [UIImage imageWithData:newImage]; } ... }
解决方法
似乎在iOS 8中返回的数据结构已经发生变化.AVMetadataItem对象的值不再是字典,而是实际的原始UIImage数据.
为NSFoundationVersionNumber添加测试可以解决问题.可能有一个更清洁的解决方案.
- (void) viewDidAppear:(BOOL)animated { self.titleText = @"Overkill"; NSString *filePath = [[NSBundle mainBundle] pathForResource:self.titleText ofType:@"mp3"]; if (!filePath) { return; } NSURL *fileURL = [NSURL fileURLWithPath:filePath]; NSLog(@"Getting song Metadata for %@",nil]; [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{ NSArray *artworks = [AVMetadataItem MetadataItemsFromArray:asset.commonMetadata withKey:AVMetadataCommonKeyArtwork keySpace:AVMetadataKeySpaceCommon]; UIImage *albumArtWork; for (AVMetadataItem *item in artworks) { if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) { // *** WE TEST THE IOS VERSION HERE *** if (TARGET_OS_IPHONE && NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1) { NSData *newImage = [item.value copyWithZone:nil]; albumArtWork = [UIImage imageWithData:newImage]; } else { NSDictionary *dict = [item.value copyWithZone:nil]; if ([dict objectForKey:@"data"]) { albumArtWork = [UIImage imageWithData:[dict objectForKey:@"data"]]; } } } else if ([item.keySpace isEqualToString:AVMetadataKeySpaceiTunes]) { // This doesn't appear to get called for images set (ironically) in iTunes albumArtWork = [UIImage imageWithData:[item.value copyWithZone:nil]]; } } if (albumArtWork != nil) { dispatch_sync(dispatch_get_main_queue(),^{ [self.albumArtImageView setImage:albumArtWork]; }); } }]; } }