iosにおける集合遍歴方法の比較とテクニック

36316 ワード

http://blog.sunnyxx.com/2014/04/30/ios_iterator/
集合の遍歴操作は開発の中で最もよく見られる操作の一つであり,C言語の古典的なforサイクルからマルチコアcpuの優位性を利用して遍歴するまで,開発の中でiosにはいくつかの集合遍歴方法があり,本稿では各操作方法の効率と優略ポテンシャルを研究とテストによって比較し,集合遍歴を用いたいくつかの小さなテクニックをまとめた.
iosでよく使われる遍歴演算方法
ループの目的は、コレクション内のオブジェクトを取得したり、アクションを実行したりすることです.この条件を満たす方法は、次のいずれかの代替として使用できます.
クラシックforサイクルfor in(NSFastEnumeration)、慣れていない場合は『nshipsterがNSFastEnumerationを紹介する文章』を参照してください.
makeObjectsPerformSelector
kvc集合演算子enumerateObjectsUsingBlock
enumerateObjectsWithOptions(NSEnumerationConcurrent)
dispatch_apply
じっけん
じっけんじょうけん
テストクラスは次のとおりです.
1
2
3
4
@interface Sark : NSObject
@property (nonatomic) NSInteger number;
- (void)doSomethingSlow; // sleep(0.01)
@end

実験は2つの側面から評価された.
それぞれ100個のオブジェクトと100000個のオブジェクトを持つNSArrayを使用し、オブジェクトのみを取り、操作を実行せずに、遍歴速度をテストする.
100個のオブジェクトを持つNSArray遍歴実行doSomethingSlowメソッドを使用して、遍歴中のマルチタスク実行速度をテストする.
実験では、CFAbsoluteTimeGetCurrent()を使用してタイムスタンプを記録し、実行時間、単位秒を計算した.iPhone 5本体(デュアルコアcpu)で動作
じっけんデータ
100オブジェクト遍歴:
1
2
3
4
5
6
7
  for   - 0.001355
for in (NSFastEnumeration) - 0.002308
makeObjectsPerformSelector - 0.001120
kvc     (@sum.number) - 0.004272 
enumerateObjectsUsingBlock - 0.001145
enumerateObjectsWithOptions(NSEnumerationConcurrent) - 0.001605
dispatch_apply(Concurrent) - 0.001380

1000000オブジェクト遍歴アクション:
1
2
3
4
5
6
7
  for   - 1.246721
for in (NSFastEnumeration) - 0.025955
makeObjectsPerformSelector - 0.068234
kvc     (@sum.number) - 21.677246
enumerateObjectsUsingBlock - 0.586034
enumerateObjectsWithOptions(NSEnumerationConcurrent) - 0.722548
dispatch_apply(Concurrent) - 0.607100

100オブジェクトの遍歴は、時間のかかる操作を実行します.
1
2
3
4
5
6
7
  for   - 1.106567
for in (NSFastEnumeration) - 1.102643
makeObjectsPerformSelector - 1.103965
kvc     (@sum.number) - N/A
enumerateObjectsUsingBlock - 1.104888
enumerateObjectsWithOptions(NSEnumerationConcurrent) - 0.554670
dispatch_apply(Concurrent) - 0.554858

注意に値する
集合中のオブジェクト数が多い場合,for in (NSFastEnumeration)の遍歴速度は非常に速いが,小規模な遍歴は明らかではない(一般的なforサイクルが速いわけではない).kvc を用いて大規模な集合を演算すると、効率が著しく低下(100万の配列がスペクトルから離れた21秒以上)し、メモリとcpu を大量に占有する.enumerateObjectsWithOptions(NSEnumerationConcurrent)およびdispatch_apply(Concurrent)の遍歴実行は、多核cpuの利点(実験における二核cpu上の効率は実質的にx 2)を利用することができる.
実践を巡るTips
ぎゃくじゅんかん遍歴NSArrayおよびNSOrderedSetは、次のように、reverseObjectEnumeratorを使用した逆ループをサポートします.
1
2
3
4
NSArray *strings = @[@"1", @"2", @"3"];
for (NSString *string in [strings reverseObjectEnumerator]) {
    NSLog(@"%@", string);
}

この方法はループで初めて呼び出されるので,ループごとの計算の問題も心配する必要はない.
また、enumerateObjectsWithOptions:NSEnumerationReverseを使用して、逆ループを実現することもできます.
1
2
3
[array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(Sark *sark, NSUInteger idx, BOOL *stop) {  [sark doSomething]; }];

blockを使用して辞書key,valueを同時に巡回する
blockバージョンの辞書遍歴はkeyとvalueを同時に取ることができます(forinはkeyを取ってから手動でvalueを取るしかありません).
1
2
3
4
NSDictionary *dict = @{@"a": @"1", @"b": @"2"};
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    NSLog(@"key: %@, value: %@", key, obj);
}];

同時バージョンを使用して、時間と順序に関係のないループを実行します.
1
2
3
[array enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(Sark *sark, NSUInteger idx, BOOL *stop) {  [sark doSomethingSlow]; }];

巡回blockはマルチコアcpuに割り当てられて実行されます(下位層はgcdの同時queueである可能性が高い)、時間のかかるタスクにとっては価値があり、後でcpuがより多くのコアにアップグレードされた後、コードを変更しなくてもメリットがあります.また、遍歴の外部は同期を保っている(遍歴が完了してから次の行を実行し続ける)ため、内部はgcdのdispatch_だろうと推測するgroupまたは信号量制御.
コードの読み取りと効率のバランス
上記のテスト結果は、集合内の要素が多くない場合、古典的なforサイクルの効率はforinよりも高いことを示しているが、コードの可読性から見ると、forinが見ているよりもはるかにスムーズではない.同様にkvcの集合演算子もあり、いくつかの内蔵操作はkeypathの方法で宣言されており、自分がforループで実現するよりも、1行のコードで済むので、はっきりしていて、重複作業を省くことができます.frameworkで集合ループのblockサポートを追加すると、indexを必要とするループには古典的なforループの書き方は二度と必要ありません.
References
http://nshipster.com/enumerators/http://iosdevelopertips.com/objective-c/fast-enumeration-on-the-iphone.html