Frage Wie iteriere ich über ein NSArray?


Ich suche nach dem Standard-Idiom, um über ein NSArray zu iterieren. Mein Code muss für OS X 10.4 und höher geeignet sein.


415
2018-06-14 14:21


Ursprung


Antworten:


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

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

Dieses Konstrukt wird verwendet, um Objekte in einer Sammlung aufzuzählen, die den [NSFastEnumeration protocol](Kakao-Referenz). Dieser Ansatz hat einen Geschwindigkeitsvorteil, da er Zeiger auf mehrere Objekte (die durch einen einzigen Methodenaufruf erhalten werden) in einem Puffer speichert und durch sie durchläuft, indem er durch den Puffer unter Verwendung von Zeigerarithmetik fortschreitet. Das ist viel schneller als das Anrufen -objectAtIndex: jedes Mal durch die Schleife.

Es ist auch erwähnenswert, dass während Sie technisch kann Verwenden Sie eine For-In-Schleife, um durch einen zu gehen NSEnumeratorIch habe herausgefunden, dass dies praktisch den gesamten Geschwindigkeitsvorteil der schnellen Aufzählung aufhebt. Der Grund ist der Standard NSEnumerator Implementierung von -countByEnumeratingWithState:objects:count: Platziert bei jedem Aufruf nur ein Objekt im Puffer.

Ich meldete dies in radar://6296108 (Die schnelle Aufzählung von NSEnumerators ist träge), wurde jedoch als Nicht behoben zurückgegeben. Der Grund ist, dass eine schnelle Enumeration eine Gruppe von Objekten vorab abruft und wenn Sie nur zu einem bestimmten Punkt im Enumerator auflisten wollen (z. B. bis ein bestimmtes Objekt gefunden wird oder eine Bedingung erfüllt ist) und denselben Enumerator verwendet, nachdem er ausgebrochen ist der Schleife würde es oft der Fall sein, dass mehrere Objekte übersprungen würden.

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

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

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


10.4 oder früher

Das Standard-Idiom für Pre-10.5 ist die Verwendung eines NSEnumerator und eine while-Schleife, so:

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 Geschwindigkeitserhöhung der Verwendung -objectAtIndex: ist zur Verbesserung mit der schnellen Aufzählung auf 10.5+ sowieso unbedeutend. (Fast Enumeration verwendet tatsächlich Zeigerarithmetik auf der zugrunde liegenden Datenstruktur und entfernt den Großteil des Methodenaufruf-Overheads.) Eine vorzeitige Optimierung ist nie eine gute Idee - sie führt zu einem unordentlicheren Code, um ein Problem zu lösen, das ohnehin nicht Ihr Engpass ist.

Beim Benutzen -objectEnumeratorSie können sehr leicht zu einer anderen aufzählbaren Sammlung wechseln (wie eine NSSet, Schlüssel in einem NSDictionaryusw.) oder sogar zu wechseln -reverseObjectEnumerator um ein Array rückwärts aufzuzählen, alles ohne andere Codeänderungen. Wenn der Iterationscode in einer Methode enthalten ist, können Sie sogar einen beliebigen übergeben NSEnumerator und der Code muss sich nicht einmal darum kümmern Was es iteriert. Weiter, ein NSEnumerator (zumindest die vom Apple-Code bereitgestellten) behält die Auflistung, die es auflistet, solange es mehr Objekte gibt, so dass Sie sich keine Gedanken darüber machen müssen, wie lange ein automatisch freigegebenes Objekt existieren wird.

Vielleicht ist das Größte daran NSEnumerator (oder schnelle Aufzählung) schützt Sie davor, dass sich eine veränderbare Sammlung (Array oder anders) unter Ihnen ändert ohne dein Wissen während du es aufzählst. Wenn Sie auf die Objekte nach Index zugreifen, können Sie auf seltsame Ausnahmen oder auf einzelne Fehler (oft lange nach dem Auftreten des Problems) stoßen, die für das Debuggen entsetzlich sein können. Enumeration mit einem der Standard-Idiome hat ein "Fail-Fast" -Verhalten, so dass das Problem (verursacht durch falschen Code) sich sofort manifestiert, wenn Sie versuchen, auf das nächste Objekt zuzugreifen, nachdem die Mutation aufgetreten ist. Wenn Programme komplexer und multi-threaded werden oder sogar von etwas abhängen, das durch Code von Drittanbietern verändert wird, werden fragile Aufzählungscodes zunehmend problematisch. Kapselung und Abstraktion FTW! :-)



629
2018-06-14 14:27



Für OS X 10.4.x und Vorgängerversionen:

 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 darüber hinaus:

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

121
2018-06-14 14:25



Die Ergebnisse des Tests und des Quellcodes sind unten aufgeführt (Sie können die Anzahl der Iterationen in der App festlegen). Die Zeit ist in Millisekunden und jeder Eintrag ist ein durchschnittliches Ergebnis des 5- bis 10-fachen Tests. Ich fand, dass es im Allgemeinen auf 2-3 signifikante Stellen genau ist und danach mit jedem Durchlauf variieren würde. Das ergibt eine Fehlermarge von weniger als 1%. Der Test lief auf einem iPhone 3G, da dies die Zielplattform war, 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 zur Verfügung gestellten Klassen für den Umgang mit Datensätzen (NSDictionary, NSArray, NSSet etc.) bieten eine sehr gute Schnittstelle für die Verwaltung von Informationen, ohne sich um die Bürokratie von Speicherverwaltung, Umverteilung usw. kümmern zu müssen . Ich denke, es ist ziemlich offensichtlich, dass die Verwendung eines NSArray von NSNumbers für einfache Iterationen langsamer ist als ein C-Array von Floats, also entschied ich mich, einige Tests zu machen, und die Ergebnisse waren ziemlich schockierend! Ich habe nicht erwartet, dass es so schlimm ist. Hinweis: Diese Tests werden auf einem iPhone 3G durchgeführt, da dies die Zielplattform ist, an der ich interessiert war.

In diesem Test mache ich einen sehr einfachen Direktzugriffsleistungsvergleich zwischen einem C float * und NSArray of NSNumbers

Ich erstelle eine einfache Schleife, um den Inhalt jedes Arrays zu summieren und sie mit mach_absolute_time () zu synchronisieren. Das NSMutableArray braucht durchschnittlich 400 mal länger !! (nicht 400 Prozent, nur 400 mal länger! Das sind 40.000% länger!).

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 ist es möglich, ein Array mit Blöcken zu iterieren. Die Syntax ist nicht so gut wie die schnelle Aufzählung, aber es gibt ein sehr interessantes Feature: gleichzeitige Aufzählung. Wenn die Aufzählungsreihenfolge nicht wichtig ist und die Jobs parallel ohne Sperren ausgeführt werden können, kann dies zu einer erheblichen Beschleunigung auf einem Mehrkernsystem führen. 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 Aufzählung ist die Verwendung eines schnellen C-Array-Zugriffs zur Optimierung der Iteration. Es soll nicht nur schneller als der traditionelle NSEnumerator sein, sondern Objective-C 2.0 bietet auch eine sehr präzise Syntax.

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

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

NSEnumerator

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

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

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

objectAtIndex: Aufzählung

Die einfachste Form der Enumeration ist die Verwendung einer for-Schleife, die eine ganze Zahl erhöht und das Objekt mit [myArray objectAtIndex: index] abfragt.

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

////////////// Von: darkdust.net


14
2018-05-11 15:30



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, Iteration Umschlag schreiben.

8
2017-08-31 07:11



Hinzufügen each Methode in Ihrem NSArray categoryDu wirst es sehr brauchen

Code aus genommen ObjectiveSugar

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

6
2018-01-01 17:14



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);
}

1
2017-07-20 16:54



Mach das :-

for (id object in array) 
{
        // statement
}

0
2018-06-01 07:17