9

我正在使用键值编码从 iTunes 中获取所有艺术家:

[self.iTunes valueForKeyPath:@"sources.@distinctUnionOfArrays.playlists.@distinctUnionOfArrays.tracks.artist"];

现在,这工作正常。这是非常有效的。我也想对这张专辑做同样的事情。

[self.iTunes valueForKeyPath:@"sources.@distinctUnionOfArrays.playlists.@distinctUnionOfArrays.tracks.album"];

这里的问题是,有多个同名专辑,但不一定是同一位艺术家。有没有办法得到每张专辑的一首歌,这样我就可以找出它是什么艺术家,也可以得到它的封面?我知道有 NSPredicate,但这很慢。

具体代码不重要,我只需要key-value编码部分。

谢谢!

4

2 回答 2

4

这不是一个完整的答案。

为了像我这样最初不知道记录在哪里的人的利益:您必须在安装了 iTunes 的 Mac 上,然后运行命令

sdef /Applications/iTunes.app | sdp -fh --basename iTunes

它将iTunes.h在您当前的工作目录中神奇地创建。然后,您可以iTunes.h浏览 API 的外观。它似乎没有正式记录在 Mac 开发人员库或任何内容中。

除了属性之外,每个iTunesTrack都有一个属性,所以我认为您真正想要做的就是返回一个唯一元组数组。artistalbum(album, artist)

好的,那么我们如何从单个 KVC 查询中获取元组数组呢?我们想写类似的东西sources.@distinctUnionOfArrays.playlists.@distinctUnionOfArrays.tracks.albumAndArtist并让它返回类似的东西NSArrayNSDictionary例如@[ @{ @"Help!", @"The Beatles" }, @{ @"No!", @"They Might Be Giants" }, ... ]

编辑ughoavgfhw 在评论中建议下一步:您可以使用类别来定义albumAndArtist,如下所示:

@interface iTunesTrack (albumAndArtist)
@property (readonly) NSDictionary *albumAndArtist;
@end

@implementation iTunesTrack (albumAndArtist)
-(NSDictionary *) albumAndArtist
{
    return @{ @"album":self.album, @"artist":self.artist };
}
@end

现在你可以写出我们试图写的那一行:

[self.iTunes valueForKeyPath:@"sources.@distinctUnionOfArrays.playlists.@distinctUnionOfArrays.tracks.albumAndArtist"];

这应该给你一个NSArrayof NSDictionary,这基本上是你想要的。请注意,我还没有测试过这段代码或任何东西!

于 2012-09-19T19:01:06.843 回答
1

@Ilija:如果你发帖说你已经放手了,那么你还没有真正放手。;) 让我看看我是否可以澄清我的评论

-(NSString *) album
{
    return self->album;
}

-(NSDictionary *) albumAndArtist
{
    return @{ @"album":self.album, @"artist":self.artist };
}

上面的album方法是 Objective-C 编译器会自动为您生成的方法。* 该albumAndArtist方法是我在回答您的原始问题时建议的。现在,如果您要求 Clang 将这两种方法降级为 C ( clang -rewrite-objc test.m -o test.cc),您会得到如下结果:

static NSDictionary * _I_iTunesTrack_albumAndArtist_albumAndArtist(iTunesTrack * self, SEL _cmd) {
    return ((NSDictionary *(*)(id, SEL, const id *, const id *, NSUInteger))(void *)
    objc_msgSend)(objc_getClass("NSDictionary"), sel_registerName("dictionaryWithObjects:forKeys:count:"),
    (const id *)__NSContainer_literal(2U, ((NSString *(*)(id, SEL))(void *)objc_msgSend)
    ((id)self, sel_registerName("album")), ((NSString *(*)(id, SEL))(void *)objc_msgSend)
    ((id)self, sel_registerName("artist"))).arr, (const id *)__NSContainer_literal(2U,
    (NSString *)&__NSConstantStringImpl_test_m_0, (NSString *)&__NSConstantStringImpl_test_m_1).arr, 2U);
}

或者,用人类的术语来说,

-(NSDictionary *) albumAndArtist
{
    id album = objc_msgSend(self, sel_registerName("album"));
    id artist = objc_msgSend(self, sel_registerName("artist"));
    id *values = calloc(2, sizeof(id)); values[0] = album; values[1] = artist;
    id *keys = calloc(2, sizeof(id)); keys[0] = @"album"; keys[1] = @"artist";
    Class dict_class = objc_getClass("NSDictionary");
    id result = objc_msgSend(dict_class, sel_registerName("dictionaryWithObjects:forKeys:count:"), values, keys, 2);
    free(values); free(keys);
    return result;
}

检查一下:三个sel_registerNames,一个objc_getClass,三个objc_msgSends,两个callocs和两个frees。album与编译器生成的方法相比,这非常低效。

从技术上讲,编译器生成的album方法如下所示:

-(NSString *) album
{
    return objc_getProperty(self, _cmd,
        __OFFSETOFIVAR__(struct iTunesTrack, album), /*atomic*/ YES);
}

但这只是因为它最初没有被声明为nonatomic. 有关 的含义objc_getProperty,请参见此处;但基本上,它比objc_msgSend本来要快。)

显然albumAndArtist会比 慢得多album,因为它正在做所有额外的工作。但是——你问——如果我们把所有的工作都扔掉然后回来self.album怎么办?好吧,生成的代码仍然比编译器为albumgetter 生成的代码更复杂:

-(NSString *) albumAndArtist_stripped_down
{
    // return self.album;
    return objc_msgSend(self, sel_registerName("album"));
}

当您的程序调用myTrack.album时,它会调用objc_msgSend一次以确定它应该调用该album方法,然后在album它内部调用objc_getProperty. 那是两个电话。(如果你数的话,三个selector_registerName("album")。)

当您的程序调用myTrack.albumAndArtist_stripped_down时,它调用objc_msgSend一次以确定它应该调用该方法albumAndArtist_stripped_down,然后调用第二次objc_msgSend然后调用. 那是三个电话。(如果算上五个。)objc_getPropertyselector_registerName

因此,对我来说,它albumAndArtist_stripped_down的速度应该是其自身速度的两倍(或 5/3 的速度)album

至于原来的albumAndArtist,仅通过计算函数调用,我预计它的速度大约是...的五倍,album但当然它会这慢得多,因为它至少进行了三个内存分配,而album没有做任何事情。内存分配和清理非常昂贵,因为malloc它本身就是一个复杂的算法。

我希望这可以为您解决问题。:)

于 2012-10-11T18:35:17.330 回答