NSArrayを反復処理するための標準的な慣用句を探しています。私のコードはOS X 10.4以降に適している必要があります。
10.5 +/iOSの一般的に推奨されるコード。
for (id object in array) {
// do something with object
}
この構造は、 NSFastEnumeration
プロトコルに準拠するコレクション内のオブジェクトを列挙するために使用されます。この方法には、複数のオブジェクト(単一のメソッド呼び出しで取得)へのポインターをバッファーに格納し、ポインター演算を使用してバッファー内を進むことでオブジェクトを反復処理するため、速度に利点があります。これはmuchループを介して毎回-objectAtIndex:
を呼び出すよりも高速です。
また、技術的にはcan for-inループを使用してNSEnumerator
をステップスルーしますが、これは高速列挙の速度の利点を事実上すべて無効にすることに注意してください。その理由は、デフォルトの-countByEnumeratingWithState:objects:count:
のNSEnumerator
実装では、呼び出しごとに1つのオブジェクトのみがバッファーに配置されるためです。
これをradar://6296108
(NSEnumeratorsの高速列挙が遅い)で報告しましたが、Not To Be Fixedとして返されました。その理由は、高速列挙はオブジェクトのグループをプリフェッチし、列挙子内の特定のポイントのみを列挙する場合(特定のオブジェクトが見つかるまで、または条件が満たされるまで)、ブレークアウト後に同じ列挙子を使用するためですループの場合、複数のオブジェクトがスキップされることがよくあります。
OS X 10.6/iOS 4.0以上をコーディングしている場合、ブロックベースのAPIを使用して配列やその他のコレクションを列挙するオプションもあります。
[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
// do something with object
}];
-enumerateObjectsWithOptions:usingBlock:
を使用して、NSEnumerationConcurrent
および/またはNSEnumerationReverse
をオプション引数として渡すこともできます。
10.5より前の標準的なイディオムは、次のようにNSEnumerator
とwhileループを使用することです。
NSEnumerator *e = [array objectEnumerator];
id object;
while (object = [e nextObject]) {
// do something with object
}
シンプルにすることをお勧めします。配列型に自分自身を結び付けることは柔軟性に欠けており、-objectAtIndex:
を使用することによる速度の向上は、とにかく10.5+での高速列挙による改善には意味がありません。 (高速列挙は、実際に基礎となるデータ構造でポインター演算を使用し、メソッド呼び出しのオーバーヘッドの大部分を削除します。)早すぎる最適化は決して良い考えではありません。それにより、ボトルネックではない問題を解決するためのコードが大きくなります。
-objectEnumerator
を使用すると、非常に簡単に別の列挙可能なコレクション(NSSet
、NSDictionary
のキーなど)に変更したり、-reverseObjectEnumerator
配列を逆方向に列挙し、他のコードを変更する必要はありません。反復コードがメソッド内にある場合、任意のNSEnumerator
を渡すことさえでき、コードはwhatを繰り返し処理する必要さえありません。さらに、NSEnumerator
(少なくともAppleコードによって提供されるもの)は、オブジェクトがさらに存在する限り、列挙しているコレクションを保持します。そのため、自動リリースされたオブジェクトがどれだけ長く存在するか心配する必要はありません。
おそらく、NSEnumerator
(または高速列挙)があなたを保護する最大の理由は、その下で変更可能なコレクション(配列など)を変更することですあなたの知識なしで列挙中です。インデックスを使用してオブジェクトにアクセスすると、奇妙な例外またはオフバイワンエラーが発生する可能性があります(多くの場合、問題が発生してからかなり時間が経過した後)。標準のイディオムのいずれかを使用した列挙には「フェイルファースト」動作があるため、突然変異が発生した後に次のオブジェクトにアクセスしようとすると、問題(不正なコードが原因)がすぐに現れます。プログラムがより複雑でマルチスレッド化されるか、サードパーティのコードが変更する可能性のあるものに依存するようになると、脆弱な列挙コードの問題がますます大きくなります。カプセル化と抽象化FTW! :-)
OS X 10.4.x以前の場合
int i;
for (i = 0; i < [myArray count]; i++) {
id myArrayElement = [myArray objectAtIndex:i];
...do something useful with myArrayElement
}
OS X 10.5.x(またはiPhone)以降の場合
for (id myArrayElement in myArray) {
...do something useful with myArrayElement
}
テストとソースコードの結果は以下のとおりです(あなたはアプリで反復回数を設定することができます)。時間はミリ秒単位で、各エントリはテストを5〜10回実行した結果の平均です。私は一般的にそれが2-3桁の有効数字まで正確であり、その後それが実行ごとに異なるだろうことを発見しました。それは1%未満の誤差範囲を与える。これは私が興味を持っていたターゲットプラットフォームなので、テストはiPhone 3Gで実行されていました。
numberOfItems NSArray (ms) C Array (ms) Ratio
100 0.39 0.0025 156
191 0.61 0.0028 218
3,256 12.5 0.026 481
4,789 16 0.037 432
6,794 21 0.050 420
10,919 36 0.081 444
19,731 64 0.15 427
22,030 75 0.162 463
32,758 109 0.24 454
77,969 258 0.57 453
100,000 390 0.73 534
データセットを扱うためにCocoaによって提供されるクラス(NSDictionary、NSArray、NSSetなど)は、メモリ管理、再割り当てなどの官僚主義を心配する必要なしに、情報を管理するためのとても素敵なインターフェースを提供します。 。 NSNumbersのNSArrayを使った方が単純な反復でCのfloat配列よりも遅くなると言うのは明らかだと思うので、いくつかのテストを行うことにしましたが、結果はかなり衝撃的でした!これが悪いとは思っていませんでした。注:これらのテストは、私が興味を持っていたターゲットプラットフォームなので、iPhone 3Gで実行されます。
このテストでは、C float *とNSArray of NSNumbersの間の非常に単純なランダムアクセスパフォーマンスの比較を行います。
各配列の内容を合計してmach_absolute_time()を使用して時間を計る単純なループを作成します。 NSMutableArrayの平均所要時間は400倍です。 (400パーセントではなく、400倍の長さです。40,000パーセントの長さです)。
ヘッダ:
// Array_Speed_TestViewController.h
//アレイスピードテスト
// Mehmet Aktenによって2009年5月2日に作成された。
// Copyright MSA Visuals Ltd. 2009.無断複写・転載を禁じます。
#import <UIKit/UIKit.h>
@interface Array_Speed_TestViewController : UIViewController {
int numberOfItems; // number of items in array
float *cArray; // normal c array
NSMutableArray *nsArray; // ns array
double machTimerMillisMult; // multiplier to convert mach_absolute_time() to milliseconds
IBOutlet UISlider *sliderCount;
IBOutlet UILabel *labelCount;
IBOutlet UILabel *labelResults;
}
-(IBAction) doNSArray:(id)sender;
-(IBAction) doCArray:(id)sender;
-(IBAction) sliderChanged:(id)sender;
@end
実装:
// Array_Speed_TestViewController.m
//アレイスピードテスト
// Mehmet Aktenによって2009年5月2日に作成された。
// Copyright MSA Visuals Ltd. 2009.無断複写・転載を禁じます。
#import "Array_Speed_TestViewController.h"
#include <mach/mach.h>
#include <mach/mach_time.h>
@implementation Array_Speed_TestViewController
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSLog(@"viewDidLoad");
[super viewDidLoad];
cArray = NULL;
nsArray = NULL;
// read initial slider value setup accordingly
[self sliderChanged:sliderCount];
// get mach timer unit size and calculater millisecond factor
mach_timebase_info_data_t info;
mach_timebase_info(&info);
machTimerMillisMult = (double)info.numer / ((double)info.denom * 1000000.0);
NSLog(@"machTimerMillisMult = %f", machTimerMillisMult);
}
// pass in results of mach_absolute_time()
// this converts to milliseconds and outputs to the label
-(void)displayResult:(uint64_t)duration {
double millis = duration * machTimerMillisMult;
NSLog(@"displayResult: %f milliseconds", millis);
NSString *str = [[NSString alloc] initWithFormat:@"%f milliseconds", millis];
[labelResults setText:str];
[str release];
}
// process using NSArray
-(IBAction) doNSArray:(id)sender {
NSLog(@"doNSArray: %@", sender);
uint64_t startTime = mach_absolute_time();
float total = 0;
for(int i=0; i<numberOfItems; i++) {
total += [[nsArray objectAtIndex:i] floatValue];
}
[self displayResult:mach_absolute_time() - startTime];
}
// process using C Array
-(IBAction) doCArray:(id)sender {
NSLog(@"doCArray: %@", sender);
uint64_t start = mach_absolute_time();
float total = 0;
for(int i=0; i<numberOfItems; i++) {
total += cArray[i];
}
[self displayResult:mach_absolute_time() - start];
}
// allocate NSArray and C Array
-(void) allocateArrays {
NSLog(@"allocateArrays");
// allocate c array
if(cArray) delete cArray;
cArray = new float[numberOfItems];
// allocate NSArray
[nsArray release];
nsArray = [[NSMutableArray alloc] initWithCapacity:numberOfItems];
// fill with random values
for(int i=0; i<numberOfItems; i++) {
// add number to c array
cArray[i] = random() * 1.0f/(Rand_MAX+1);
// add number to NSArray
NSNumber *number = [[NSNumber alloc] initWithFloat:cArray[i]];
[nsArray addObject:number];
[number release];
}
}
// callback for when slider is changed
-(IBAction) sliderChanged:(id)sender {
numberOfItems = sliderCount.value;
NSLog(@"sliderChanged: %@, %i", sender, numberOfItems);
NSString *str = [[NSString alloc] initWithFormat:@"%i items", numberOfItems];
[labelCount setText:str];
[str release];
[self allocateArrays];
}
//cleanup
- (void)dealloc {
[nsArray release];
if(cArray) delete cArray;
[super dealloc];
}
@end
投稿者:memo.tv
////////////////////// ////////////////////
ブロックの導入以来利用可能で、これはブロックで配列を繰り返すことを可能にします。その構文は高速列挙型ほど素敵ではありませんが、非常に興味深い機能が1つあります。それは並行列挙型です。列挙順序が重要ではなく、ジョブをロックせずに並行して実行できる場合は、マルチコアシステムの処理速度が大幅に向上します。これについては、同時列挙型セクションで詳しく説明しています。
[myArray enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
[self doSomethingWith:object];
}];
[myArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[self doSomethingWith:object];
}];
/////////// NSFastEnumerator
高速列挙の背後にある考え方は、反復を最適化するために高速C配列アクセスを使用することです。従来のNSEnumeratorよりも高速であると想定されているだけでなく、Objective-C 2.0では非常に簡潔な構文も提供されています。
id object;
for (object in myArray) {
[self doSomethingWith:object];
}
/////////////////
NSEnumerator
これは外部反復の形式です。[myArray objectEnumerator]はオブジェクトを返します。このオブジェクトにはnextObjectというメソッドがあり、それをnilが返されるまでループ内で呼び出すことができます。
NSEnumerator *enumerator = [myArray objectEnumerator];
id object;
while (object = [enumerator nextObject]) {
[self doSomethingWith:object];
}
/////////////////
objectAtIndex:列挙
整数を増やすforループを使用し、[myArray objectAtIndex:index]を使用してオブジェクトを照会するのが最も基本的な列挙型です。
NSUInteger count = [myArray count];
for (NSUInteger index = 0; index < count ; index++) {
[self doSomethingWith:[myArray objectAtIndex:index]];
}
//////////////投稿者:darkdust.net
3つの方法があります:
//NSArray
NSArray *arrData = @[@1,@2,@3,@4];
// 1.Classical
for (int i=0; i< [arrData count]; i++){
NSLog(@"[%d]:%@",i,arrData[i]);
}
// 2.Fast iteration
for (id element in arrData){
NSLog(@"%@",element);
}
// 3.Blocks
[arrData enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"[%lu]:%@",idx,obj);
// Set stop to YES in case you want to break the iteration
}];
NSArray category
にeach
メソッドを追加してください、あなたはそれをたくさん必要とするでしょう
ObjectiveSugar から取得したコード
- (void)each:(void (^)(id object))block {
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
block(obj);
}];
}
これが、文字列の配列を宣言し、それらを反復処理する方法です。
NSArray *langs = @[@"es", @"en", @"pt", @"it", @"fr"];
for (int i = 0; i < [langs count]; i++) {
NSString *lang = (NSString*) [langs objectAtIndex:i];
NSLog(@"%@, ",lang);
}
スイフト用
let arrayNumbers = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
// 1
for (index, value) in arrayNumbers.enumerated() {
print(index, value)
//... do somthing with array value and index
}
//2
for value in arrayNumbers {
print(value)
//... do somthing with array value
}