Objective C keypath从iTunes中获取所有艺术家

时间:2012-09-10 21:18:40

标签: objective-c nspredicate enumeration key-value-coding

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

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

现在,这很好用。 这非常有效。我也想对专辑做同样的事情。

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

这里的问题是,有多个相同名称的专辑,但不一定是同一个艺术家。有没有办法获得每张专辑的歌曲,所以我可以找出它是什么艺术家,并获得它的封面? 我知道有NSPredicate,但这很慢。

具体代码并不重要,我只需要键值编码部分。

谢谢!

2 个答案:

答案 0 :(得分:4)

这不是一个完整的答案。

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

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

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

除了iTunesTrack属性之外,每个artist都有一个album属性,所以我认为你真正想做的就是返回一组唯一元组{{1} }。

好的,那么我们如何才能从单个KVC查询中获取一组元组?我们想写一些类似(album, artist)的内容,并返回sources.@distinctUnionOfArrays.playlists.@distinctUnionOfArrays.tracks.albumAndArtist NSArray之类的内容,例如NSDictionary

编辑 ughoavgfhw建议评论的下一步:您可以使用类别定义@[ @{ @"Help!", @"The Beatles" }, @{ @"No!", @"They Might Be Giants" }, ... ],如下所示:

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"]; NSArray,这基本上就是你想要的。请注意,我没有测试过此代码或其他任何内容!

答案 1 :(得分:1)

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

-(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_registerName,一个objc_getClass,三个objc_msgSend,两个calloc和两个free。与编译器生成的album方法相比,这是非常低效的。

  

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

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

但这只是因为它最初没有被声明为nonatomic。有关objc_getProperty的含义,请参阅here;但基本上,它比objc_msgSend更快。)

因此,albumAndArtist显然比album要慢得多,因为它正在做的所有额外工作。但是 - 你问 - 如果我们摆脱所有这些工作并返回self.album怎么办?好吧,生成的代码仍然比编译器为album getter生成的代码更加毛茸茸:

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

当您的程序调用{​​{1}}时,它会调用myTrack.album一次以确定它应该调用objc_msgSend方法,然后在album内调用album }。这是两个电话。 (如果算上objc_getProperty则为三。)

当您的程序调用{​​{1}}时,它会调用selector_registerName("album")一次,以确定它应该调用myTrack.albumAndArtist_stripped_down方法,然后 调用{{1}第二次,然后 调用objc_msgSend。这是三个电话。 (如果算上albumAndArtist_stripped_down,则为五。)

所以我觉得objc_msgSend本身应该比objc_getProperty慢两倍(或慢5/3)。

至于原始selector_registerName,只需计算函数调用,我预计它的速度大约是albumAndArtist_stripped_down的五倍......但当然它会< em>很多慢于此,因为它至少进行了三次内存分配,而album没有做任何事情。内存分配和清理非常非常昂贵,因为albumAndArtist本身就是一个复杂的算法。

我希望这能为你解决问题。 :)