NSArray を反復処理するにはどうすればいいですか? 質問する

NSArray を反復処理するにはどうすればいいですか? 質問する

を反復処理するための標準的な慣用句を探していますNSArray。コードは OS X 10.4 以降に適合している必要があります。

ベストアンサー1

10.5+/iOS で一般的に推奨されるコード。

for (id object in array) {
    // do something with object
}

この構造は、コレクション内のオブジェクトを列挙するために使用されます。NSFastEnumerationプロトコル。このアプローチでは、複数のオブジェクトへのポインター (単一のメソッド呼び出しで取得) をバッファーに格納し、ポインター演算を使用してバッファー内を進むことでそれらを反復処理するため、速度上の利点があります。これは、ループを介して毎回呼び出すよりもはるかに高速です。-objectAtIndex:

また、技術的にはfor-in ループを使用して をステップ実行できますNSEnumeratorが、これによって高速列挙の速度上の利点が実質的にすべて無効になることにも注意してください。その理由は、 のデフォルトのNSEnumerator実装では、-countByEnumeratingWithState:objects:count:各呼び出しでバッファーにオブジェクトが 1 つだけ配置されるためです。

私はこれを (NSEnumerator の高速列挙が遅い) で報告しましたradar://6296108が、修正不可として返されました。その理由は、高速列挙ではオブジェクトのグループを事前に取得し、列挙子の特定のポイント (特定のオブジェクトが見つかる、または条件が満たされるまでなど) までのみ列挙し、ループから抜け出した後も同じ列挙子を使用する場合、複数のオブジェクトがスキップされるケースが頻繁に発生するためです。

OS X 10.6 / iOS 4.0 以降向けにコーディングしている場合は、ブロックベースの API を使用して配列やその他のコレクションを列挙することもできます。

[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
    // do something with object
}];

また、 and/or をオプション引数として使用し-enumerateObjectsWithOptions:usingBlock:て渡すこともできます。NSEnumerationConcurrentNSEnumerationReverse


10.4以前

10.5 より前の標準的な慣用句はNSEnumerator、次のように and while ループを使用することです。

NSEnumerator *e = [array objectEnumerator];
id object;
while (object = [e nextObject]) {
  // do something with object
}

シンプルにしておくことをお勧めします。配列型に縛られると柔軟性がなくなり、使用することで得られるとされる速度向上は、-objectAtIndex:10.5 以降の高速列挙による改善に比べれば取るに足らないものになります。(高速列挙は実際には基礎となるデータ構造でポインタ演算を使用し、メソッド呼び出しのオーバーヘッドのほとんどを排除します。) 時期尚早な最適化は決して良い考えではありません。ボトルネックではない問題を解決するために、より乱雑なコードになってしまうからです。

を使用すると、他の列挙可能なコレクション ( 、 内のキーなど)-objectEnumeratorに簡単に変更したり、 に切り替えて配列を逆順に列挙したりすることができます。これらはすべて、他のコードを変更することなく実行できます。反復コードが メソッド内にある場合は、 any を渡すこともでき、コードは反復処理の対象を気にする必要さえありません。さらに、 (少なくとも Apple コードで提供されるもの) は、オブジェクトがさらに存在する限り列挙しているコレクションを保持するため、自動解放されたオブジェクトが存在する期間を気にする必要はありません。NSSetNSDictionary-reverseObjectEnumeratorNSEnumeratorNSEnumerator

おそらく、(または高速列挙) によって保護される最大のものは、列挙中に知らないうちNSEnumeratorに可変コレクション (配列など) が変更されることです。オブジェクトにインデックスでアクセスすると、奇妙な例外や 1 つずれたエラー (問題が発生してからかなり経ってから) に遭遇する可能性があり、デバッグが困難になる可能性があります。標準のイディオムの 1 つを使用した列挙には「フェイル ファスト」動作があるため、(誤ったコードによって発生した) 問題は、変更が発生した後に次のオブジェクトにアクセスしようとするとすぐに現れます。プログラムが複雑になり、マルチスレッド化されるか、サードパーティのコードによって変更される可能性のあるものに依存するようになると、脆弱な列挙コードがますます問題になります。カプセル化と抽象化は最高です! :-)


おすすめ記事