it-swarm.com.de

Wie durchlaufe ich ein NSArray?

Ich bin auf der Suche nach der Standardsprache, um einen NSArray zu durchlaufen. Mein Code muss für OS X 10.4+ geeignet sein.

431
Steve McLeod

Der allgemein bevorzugte Code für 10.5 +/iOS.

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

Dieses Konstrukt wird zum Auflisten von Objekten in einer Auflistung verwendet, die dem Protokoll NSFastEnumeration entspricht. Dieser Ansatz hat einen Geschwindigkeitsvorteil, da er Zeiger auf mehrere Objekte (die über einen einzigen Methodenaufruf abgerufen werden) in einem Puffer speichert und durchläuft, indem er mit Hilfe der Zeigerarithmetik durch den Puffer vorrückt. Dies ist viel schneller als der Aufruf von -objectAtIndex: jedes Mal durch die Schleife.

Es ist auch erwähnenswert, dass, während Sie technisch {can eine for-in-Schleife verwenden, um durch ein NSEnumerator zu blättern, ich festgestellt habe, dass dies praktisch den gesamten Geschwindigkeitsvorteil der schnellen Aufzählung zunichte macht. Der Grund ist, dass die standardmäßige NSEnumerator-Implementierung von -countByEnumeratingWithState:objects:count: bei jedem Aufruf nur ein Objekt im Puffer platziert.

Ich habe dies in radar://6296108 gemeldet (die schnelle Aufzählung von NSEnumerators ist schleppend), wurde aber als nicht zu beheben zurückgegeben. Der Grund ist, dass die schnelle Aufzählung eine Gruppe von Objekten abruft, und wenn Sie nur bis zu einem bestimmten Punkt im Enumerator auflisten möchten (z. B. bis ein bestimmtes Objekt gefunden oder die Bedingung erfüllt ist) und nach dem Ausbruch denselben Enumerator verwenden In der Schleife würden oft mehrere Objekte übersprungen werden.

Wenn Sie für OS X 10.6/iOS 4.0 und höher codieren, haben Sie auch die Möglichkeit, blockbasierte APIs zum Auflisten von Arrays und anderen Sammlungen zu verwenden:

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

Sie können auch -enumerateObjectsWithOptions:usingBlock: verwenden und NSEnumerationConcurrent und/oder NSEnumerationReverse als Optionsargument übergeben.


10.4 oder früher

Die Standardsprache für pre-10.5 ist die Verwendung einer NSEnumerator- und einer while-Schleife, wie folgt:

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

Ich empfehle, es einfach zu halten. Sich an einen Array-Typ zu binden, ist unflexibel, und die angebliche Geschwindigkeitssteigerung bei der Verwendung von -objectAtIndex: ist ohnehin unerheblich für die Verbesserung, da auf 10.5+ ohnehin eine schnelle Aufzählung erfolgt. (Die schnelle Aufzählung verwendet tatsächlich die Zeigerarithmetik für die zugrunde liegende Datenstruktur und entfernt den Großteil des Overheads der Methodenaufrufe.) Vorzeitige Optimierung ist nie eine gute Idee - sie führt zu unübersichtlichem Code, um ein Problem zu lösen, das ohnehin nicht Ihr Engpass ist.

Bei Verwendung von -objectEnumerator können Sie sehr leicht zu einer anderen aufzählbaren Auflistung wechseln (z. B. NSSet, Schlüssel in NSDictionary usw.) oder sogar zu -reverseObjectEnumerator wechseln, um ein Array rückwärts aufzuzählen, alle ohne anderen Code Änderungen. Wenn der Iterationscode in einer Methode enthalten ist, können Sie sogar NSEnumerator übergeben, und der Code muss sich nicht einmal darum kümmern, dass was iteriert wird. Darüber hinaus behält ein NSEnumerator (zumindest die durch Apple-Code bereitgestellten) die Auflistung der Auflistung bei, solange weitere Objekte vorhanden sind, sodass Sie sich keine Gedanken darüber machen müssen, wie lange ein automatisch freigegebenes Objekt vorhanden ist.

Die größte Sache, vor der ein NSEnumerator (oder eine schnelle Aufzählung) Sie davon abhält, besteht darin, dass Sie eine veränderliche Auflistung (Array oder sonstiges) unter sich ändern ohne Ihr Wissen, während Sie sie auflisten. Wenn Sie über Index auf die Objekte zugreifen, können Sie zu seltsamen Ausnahmen oder Off-by-One-Fehlern kommen (oft lange nachdem das Problem aufgetreten ist), deren Debuggen fürchterlich sein kann. Die Aufzählung mit einem der Standard-Idiome hat ein "ausfallsicheres" Verhalten. Daher wird das Problem (verursacht durch falschen Code) sofort angezeigt, wenn Sie versuchen, auf das nächste Objekt zuzugreifen, nachdem die Mutation aufgetreten ist. Da Programme immer komplexer und multithreading werden oder sogar von etwas abhängen, das von Drittanbietercode geändert werden kann, wird fragiler Aufzählungscode zunehmend problematischer. Kapselung und Abstraktion FTW! :-)


646
Quinn Taylor

Für OS X 10.4.x und älter:

 int i;
 for (i = 0; i < [myArray count]; i++) {
   id myArrayElement = [myArray objectAtIndex:i];
   ...do something useful with myArrayElement
 }

Für OS X 10.5.x (oder iPhone) und höher:

for (id myArrayElement in myArray) {
   ...do something useful with myArrayElement
}
124
diederikh

Die Ergebnisse des Test- und Quellcodes sind unten aufgeführt (Sie können die Anzahl der Iterationen in der App festlegen). Die Zeit wird in Millisekunden angegeben, und jeder Eintrag ist ein durchschnittliches Ergebnis, wenn der Test 5-10 Mal ausgeführt wird. Ich fand, dass es im Allgemeinen auf 2-3 signifikante Stellen genau ist und danach bei jedem Durchlauf variieren würde. Dies ergibt eine Fehlerquote von weniger als 1%. Der Test lief auf einem iPhone 3G, da dies die Zielplattform ist, an der ich interessiert war.

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

Die von Cocoa bereitgestellten Klassen für den Umgang mit Datensätzen (NSDictionary, NSArray, NSSet usw.) bieten eine sehr schöne Schnittstelle zum Verwalten von Informationen, ohne sich um die Bürokratie des Speichermanagements, der Neuzuweisung usw. kümmern zu müssen . Ich denke, es ist ziemlich offensichtlich, dass die Verwendung eines NSArray von NSNumbers für einfache Iterationen langsamer sein wird als ein C-Array von Floats. Ich entschied mich also für einige Tests und die Ergebnisse waren ziemlich schockierend! Ich hatte nicht erwartet, dass es so schlimm sein würde. Hinweis: Diese Tests werden auf einem iPhone 3G durchgeführt, da dies die Zielplattform ist, an der ich interessiert war.

In diesem Test führe ich einen einfachen Vergleich des Direktzugriffs zwischen einem C-Float * und einem NS-Array von NS-Nummern durch

Ich erstelle eine einfache Schleife, um den Inhalt jedes Arrays zusammenzufassen und die Zeit mit mach_absolute_time () festzulegen. Der NSMutableArray dauert durchschnittlich 400 mal länger !! (nicht 400 Prozent, nur 400 mal länger! das sind 40.000% mehr!).

Header:

// Array_Speed_TestViewController.h

// Array-Geschwindigkeitstest

// Erstellt von Mehmet Akten am 02.05.2009.

// Copyright MSA Visuals Ltd. 2009. Alle Rechte vorbehalten.

#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

Implementierung:

// Array_Speed_TestViewController.m

// Array-Geschwindigkeitstest

// Erstellt von Mehmet Akten am 02.05.2009.

// Copyright MSA Visuals Ltd. 2009. Alle Rechte vorbehalten.

    #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

Von: memo.tv

////////////////////

Seit der Einführung von Blöcken verfügbar, kann ein Array mit Blöcken iteriert werden. Seine Syntax ist nicht so schön wie die schnelle Aufzählung, aber es gibt eine sehr interessante Funktion: die gleichzeitige Aufzählung. Wenn die Aufzählungsreihenfolge nicht wichtig ist und die Jobs parallel ohne Sperren ausgeführt werden können, kann dies bei einem Multicore-System eine erhebliche Beschleunigung bewirken. Mehr dazu im Abschnitt zur gleichzeitigen Aufzählung.

[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

Die Idee hinter der schnellen Enumeration ist der schnelle Zugriff auf C-Arrays, um die Iteration zu optimieren. Es soll nicht nur schneller als herkömmlicher NSEnumerator sein, sondern auch mit Objective-C 2.0 eine sehr prägnante Syntax.

id object;
for (object in myArray) {
    [self doSomethingWith:object];
}

/////////////////

NSEnumerator

Dies ist eine Form der externen Iteration: [meinArray objectEnumerator] gibt ein Objekt zurück. Dieses Objekt hat eine Methode nextObject, die wir in einer Schleife aufrufen können, bis es nil zurückgibt

NSEnumerator *enumerator = [myArray objectEnumerator];
id object;
while (object = [enumerator nextObject]) {
    [self doSomethingWith:object];
}

/////////////////

objectAtIndex: Aufzählung

Die Verwendung einer for-Schleife, die eine Ganzzahl erhöht, und die Abfrage des Objekts mit [myArray objectAtIndex: index] ist die grundlegendste Form der Aufzählung.

NSUInteger count = [myArray count];
for (NSUInteger index = 0; index < count ; index++) {
    [self doSomethingWith:[myArray objectAtIndex:index]];
}

////////////Ausführung: darkdust.net

15
Hitendra Hckr

Die drei Möglichkeiten sind: 

        //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. Ist der schnellste Weg in der Ausführung, und 3. mit Autovervollständigung vergessen Sie das Schreiben des Iterationskollapses.

Fügen Sie die each-Methode in Ihren NSArray category-Code ein

Code aus ObjectiveSugar

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

So deklarieren Sie ein Array von Strings und iterieren über sie:

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

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

Für Swift 

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