在iOS 8上获取mp3封面会崩溃,但是在iOS 7上可以正常工作

9

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

我曾经在我的iOS 7.1的iPhone 5上使用此SO问题中的代码来加载mp3文件中的专辑封面。

但后来,在iOS模拟器中发现了与这个代码有关的崩溃情况。进一步调查发现,这个代码也会使我的iPad崩溃。在将其升级到iOS 8之后,它会导致iPad崩溃。

看起来包含图像的字典已受损。

我创建了一个虚拟的iOS项目,其中加载专辑封面并得到了相同的结果。下面是来自该视图控制器的代码:

- (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的文件。我用从iTunes和Amazon导出的多个mp3和m4a文件进行了测试,所以我知道这些文件本身是被正确编码的。
在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];
            }
            ...
        }

啊...Xcode 6.0。必须告诉你,我在刚开始学习的时候写了那段代码。自那以后很多东西都发生了变化。请尝试记录item.valueitem以及通过复制item.value创建的字典。另外,请发布回溯和崩溃日志本身好吗? - duci9y
@duci9y 感谢你提供的指引!请看我上面的更新。在模拟器或iPad上使用时,item.value似乎是原始的UIImage数据(而不是NSDictionary)。非常奇怪。你知道有没有一种方法可以确定UIImage与NSDictionary而不会使应用程序崩溃吗?(我正在研究这个问题。) - Stan James
使用AVMetadataID3MetadataKeyAttachedPicture来处理MP3文件,使用AVMetadataiTunesMetadataKeyCoverArt来处理iTunes文件,而不是常用的键,看看是否能获得更可预测的结果。 - duci9y
1个回答

17

看起来iOS 8中返回的数据结构已经发生了变化。 AVMetadataItem 对象的 value 不再是字典,而是实际的原始 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 %@", 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];
                });
            }

        }];
    }

}

1
非常感谢你,Stan!有时候我觉得想要跳上一架飞往旧金山的飞机,去掐几个苹果公司的员工。他们为什么觉得需要浪费我们的时间来搞这种无聊的事情,我永远不会理解。 - amergin
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Fahri Azimov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接