9

キー値コーディングを使用して、iTunes からすべてのアーティストを取得しています。

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

これで問題なく動作します。これは非常に効率的です。アルバムも同じようにやりたいです。

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

ここでの問題は、同じ名前のアルバムが複数あることですが、必ずしも同じアーティストのものであるとは限りません。各アルバムの曲を取得する方法はありますか? そのアーティストが何であるかを調べたり、そのカバーを取得したりできますか? NSPredicate があることは知っていますが、これは非常に遅いです。

特定のコードは重要ではありません。キーと値のコーディング部分だけが必要です。

ありがとうございました!

4

2 に答える 2

4

これは完全な答えではありません。

これがどこに文書化されているかを最初に知らなかった私のような人々の利益のために: iTunes がインストールされた Mac 上にいて、コマンドを実行する必要があります。

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

iTunes.h現在の作業ディレクトリに魔法のように作成されます。その後、トロールしiTunes.hて API がどのように表示されるかを確認できます。Mac Developer Library などで公式に文書化されていないようです。

それぞれiTunesTrackartistプロパティに加えてプロパティを持っているalbumので、本当にやりたいことは一意のタプルの配列を返すことだけだと思います(album, artist)

では、単一の KVC クエリからタプルの配列を取得するにはどうすればよいでしょうか? のようなものを書き、 of のsources.@distinctUnionOfArrays.playlists.@distinctUnionOfArrays.tracks.albumAndArtistようなものを返すようにしたいと思います。たとえば、NSArrayNSDictionary@[ @{ @"Help!", @"The Beatles" }, @{ @"No!", @"They Might Be Giants" }, ... ]

EDIT 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"];

これは基本的にあなたNSArrayNSDictionary望んでいたものです。注意してください、私はこのコードなどをテストしていません!

于 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にこれら2つのメソッドを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;
}

それをチェックしてください:3sel_registerName秒、1秒objc_getClass、3objc_msgSend秒、2calloc秒、および2free秒。これは、コンパイラで生成されたalbumメソッドと比較すると、かなり非効率的です。

技術的には、コンパイラによって生成されるalbumメソッドは次のようになります。

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

しかし、それは元々がとして宣言されていなかったからnonatomicです。の意味については、ここobjc_getPropertyを参照してください; しかし、基本的には、以前よりも高速です。)objc_msgSend

そのため、余分な作業が行われているため、明らかに、albumAndArtistよりもはるかに遅くなります。albumしかし—あなたは尋ねます—もし私たちがそのすべての仕事を取り除き、ただ戻ったらどうなるself.albumでしょうか?さて、生成されたコードは、コンパイラがalbumゲッター用に生成したものよりもまだ毛深いです:

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

プログラムがを呼び出すとmyTrack.album、プログラムはメソッドobjc_msgSendを呼び出す必要があることを理解するために1回呼び出しalbum、次にその内部albumでを呼び出しますobjc_getProperty。それは2つの呼び出しです。(数えると3つselector_registerName("album")。)

プログラムがを呼び出すとmyTrack.albumAndArtist_stripped_down、 1回呼び出してメソッドobjc_msgSendを呼び出す必要があることを確認し、次に2回目の呼び出しを行っから、を呼び出します。それは3つの呼び出しです。(数えると5つ。)albumAndArtist_stripped_downobjc_msgSendobjc_getPropertyselector_registerName

albumAndArtist_stripped_downしたがって、それ自体の約2倍の速度(または5/3の速度)にする必要があることは私には理にかなっていalbumます。

オリジナルalbumAndArtistの場合、関数呼び出しを数えるだけで、約5倍遅くなると思いますが、少なくとも3つのメモリ割り当てを行っているため、albumもちろんそれよりもはるかに遅くなります。一方、album何もしていません。はそれ自体が複雑なアルゴリズムであるため、メモリの割り当てとクリーンアップは非常にコストがかかります。malloc

これで問題が解決することを願っています。:)

于 2012-10-11T18:35:17.330 に答える