web-dev-qa-db-ja.com

NSArrayを反復処理する方法

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

435
Steve McLeod

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.4以前

10.5より前の標準的なイディオムは、次のようにNSEnumeratorとwhileループを使用することです。

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

シンプルにすることをお勧めします。配列型に自分自身を結び付けることは柔軟性に欠けており、-objectAtIndex:を使用することによる速度の向上は、とにかく10.5+での高速列挙による改善には意味がありません。 (高速列挙は、実際に基礎となるデータ構造でポインター演算を使用し、メソッド呼び出しのオーバーヘッドの大部分を削除します。)早すぎる最適化は決して良い考えではありません。それにより、ボトルネックではない問題を解決するためのコードが大きくなります。

-objectEnumeratorを使用すると、非常に簡単に別の列挙可能なコレクション(NSSetNSDictionaryのキーなど)に変更したり、-reverseObjectEnumerator配列を逆方向に列挙し、他のコードを変更する必要はありません。反復コードがメソッド内にある場合、任意のNSEnumeratorを渡すことさえでき、コードはwhatを繰り返し処理する必要さえありません。さらに、NSEnumerator(少なくともAppleコードによって提供されるもの)は、オブジェクトがさらに存在する限り、列挙しているコレクションを保持します。そのため、自動リリースされたオブジェクトがどれだけ長く存在するか心配する必要はありません。

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


661
Quinn Taylor

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
}
124
diederikh

テストとソースコードの結果は以下のとおりです(あなたはアプリで反復回数を設定することができます)。時間はミリ秒単位で、各エントリはテストを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

16
Hitendra Hckr

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
    }];
  1. 実行の最速の方法であり、そして3.自動補完を使って、繰り返しエンベロープを書くことを忘れる。

NSArray categoryeachメソッドを追加してください、あなたはそれをたくさん必要とするでしょう

ObjectiveSugar から取得したコード

- (void)each:(void (^)(id object))block {
    [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        block(obj);
    }];
}
6
onmyway133

これが、文字列の配列を宣言し、それらを反復処理する方法です。

NSArray *langs = @[@"es", @"en", @"pt", @"it", @"fr"];

for (int i = 0; i < [langs count]; i++) {
  NSString *lang = (NSString*) [langs objectAtIndex:i];
  NSLog(@"%@, ",lang);
}
3
oabarca

スイフト用

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
}
0
Nilesh R Patel