我正在使用键值编码来获取iTunes中的所有艺术家:
[self.iTunes valueForKeyPath:@"sources.@distinctUnionOfArrays.playlists.@distinctUnionOfArrays.tracks.artist"];
现在,这很好用。 这非常有效。我也想对专辑做同样的事情。
[self.iTunes valueForKeyPath:@"sources.@distinctUnionOfArrays.playlists.@distinctUnionOfArrays.tracks.album"];
这里的问题是,有多个相同名称的专辑,但不一定是同一个艺术家。有没有办法获得每张专辑的歌曲,所以我可以找出它是什么艺术家,并获得它的封面? 我知道有NSPredicate,但这很慢。
具体代码并不重要,我只需要键值编码部分。
谢谢!
答案 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
本身就是一个复杂的算法。
我希望这能为你解决问题。 :)