本文介绍了获取mp3图片在iOS 8上崩溃,但适用于iOS 7的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑:罪魁祸首是iOS 8,而不是模拟器(我没有意识到已经在运行iOS 8)我已经重命名了标题以反映这一点。



我很高兴使用中的代码从mp3文件中加载专辑图片。这是在我的带有iOS 7.1的iPhone 5上。



然后我跟踪iOS模拟器中的崩溃到这段代码。进一步的调查显示,这段代码也在我的iPad上崩溃了。升级到iOS 8后,它在我的iPad上崩溃。



包含图像的字典似乎已损坏。



我创建了一个虚拟iOS项目,加载专辑封面并得到相同的结果。下面是该viewcontroller的代码。

   - (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(@获取%@的歌曲元数据,self.titleText);
AVAsset * asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
if(asset!= nil){
NSArray * keys = [NSArray arrayWithObjects:@commonMetadata,nil];
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^ {
NSArray * artwork = [AVMetadataItem metadataItemsFromArray:asset.commonMetadata
withKey:AVMetadataCommonKeyArtwork
keySpace:AVMetadataKeySpaceCommon];
UIImage * albumArtWork;

for(AVMetadataItem *艺术品中的项目){
if([item.keySpace isEqualToString:AVMetadataKeySpaceID3]){
NSDictionary * dict = [item.value copyWithZone:nil];

// **********
//使用SIGABRT崩溃。 dict不是有效的字典。
// **********

if([dict objectForKey:@data]){
albumArtWork = [UIImage imageWithData:[dict objectForKey :@数据]];
}
}
else if([item.keySpace isEqualToString:AVMetadataKeySpaceiTunes]){
//这似乎没有被调用的图像集(具有讽刺意味)在iTunes
albumArtWork = [UIImage imageWithData:[item.value copyWithZone:nil]];
}
}

if(albumArtWork!= nil){
dispatch_sync(dispatch_get_main_queue(),^ {
[self.albumArtImageView setImage:albumArtWork] ;
});
}

}];
}

}

我已标记为碰撞。它希望文件 Overkill.mp3 在捆绑中。我测试了从iTunes和亚马逊导出的多个mp3和m4a,所以我知道文件本身是正确编码的。



在Xcode 6.0和6.1中测试过。



为什么它可以在iPhone而不是模拟器或iPad上运行?



EDIT / UPDATE:



记录item.value会显示差异。



在iPhone 5上(正常):

 (lldb)po item.value 
{
MIME = JPG;
data =< ffd8ffe0 ....几Kb的数据..... 2a17ffd9> ;;
identifier =;
picturetype =其他;
}

在模拟器上(崩溃)

 (lldb)po item.value 
< ffd8ffe0 ....几Kb的数据..... 2a17ffd9>

因此,模拟器上似乎没有字典,只有原始图稿。



将代码更改为而不是需要字典,但需要 item.value as一个UIImage 工作

  for(AVMetadataItem *艺术品中的项目){
if( [item.keySpace isEqualToString:AVMetadataKeySpaceID3]){
NSData * newImage = [item.value copyWithZone:nil];
albumArtWork = [UIImage imageWithData:newImage];
}
...
}


解决方案

似乎返回的数据结构在iOS 8中已更改。 AVMetadataItem 对象的不再是字典,而是实际的原始 UIImage 数据。



NSFoundationVersionNumber 添加测试可以解决问题。可能有一个更清洁的解决方案。

   - (void)viewDidAppear:(BOOL)动画
{
self.titleText = @Overkill;
NSString * filePath = [[NSBundle mainBundle] pathForResource:self.titleText ofType:@mp3];
if(!filePath){
return;
}
NSURL * fileURL = [NSURL fileURLWithPath:filePath];

NSLog(@获取%@的歌曲元数据,self.titleText);
AVAsset * asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
if(asset!= nil){
NSArray * keys = [NSArray arrayWithObjects:@commonMetadata,nil];
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^ {
NSArray * artwork = [AVMetadataItem metadataItemsFromArray:asset.commonMetadata
withKey:AVMetadataCommonKeyArtwork
keySpace:AVMetadataKeySpaceCommon];
UIImage * albumArtWork;

for(AVMetadataItem *艺术品中的项目){
if([item.keySpace isEqualToString:AVMetadataKeySpaceID3]){

// ***我们测试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]){
//这似乎没有为图像集调用(具有讽刺意味的是)在iTunes中
albumArtWork = [UIImage imageWithData:[item.value copyWithZone:nil]];
}
}

if(albumArtWork!= nil){
dispatch_sync(dispatch_get_main_queue(),^ {
[self.albumArtImageView setImage:albumArtWork] ;
});
}

}];
}

}


EDIT: The culprit was iOS 8, not the simulator (which I didn't realize was already running iOS 8) I've renamed the title to reflect this.

I was happily using the code from this SO question to load album artwork from mp3 files. This was on my iPhone 5 with iOS 7.1.

But then I traced crashing in the iOS simulator to this code. Further investigation revealed that this code also crashed on my iPad. It crashed on my iPad after upgrading it to iOS 8.

It appears the dictionary containing the image is corrupted.

I created a dummy iOS project that only loads album art and got the same result. Below is the code from that 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];
                });
            }

        }];
    }

}

I've marked the line with the crash. It expects a file Overkill.mp3 to be in the bundle. I tested with multiple mp3's and m4a's exported from iTunes and Amazon, so I know the files themselves are correctly encoded.

Tested in Xcode 6.0 and 6.1.

Any ideas why it would work on iPhone but not the simulator or iPad?

EDIT / UPDATE:

Logging the item.value reveals differences.

On iPhone 5 (works):

(lldb) po item.value
{
    MIME = JPG;
    data = <ffd8ffe0 .... several Kb of data ..... 2a17ffd9>;
    identifier = "";
    picturetype = Other;
}

On Simulator (crashes)

(lldb) po item.value
<ffd8ffe0 .... several Kb of data ..... 2a17ffd9>

So it appears that on the simulator there is no dictionary, just the raw artwork.

Changing the code to not expect a dictionary, but take item.value as a UIImage works!

        for (AVMetadataItem *item in artworks) {
            if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) {
                NSData *newImage = [item.value copyWithZone:nil];
                albumArtWork = [UIImage imageWithData:newImage];
            }
            ...
        }
解决方案

It seems the returned data structure has changed in iOS 8. The value of the AVMetadataItem object is no longer a dictionary, but the actual raw UIImage data.

Adding a test for the NSFoundationVersionNumber solves the problem. There is probably a cleaner solution.

- (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 %@", 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]) {

                    // *** 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];
                });
            }

        }];
    }

}

这篇关于获取mp3图片在iOS 8上崩溃,但适用于iOS 7的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-23 19:12