Come posso iterare su un NSArray?


Risposte:


667

Il codice generalmente preferito per 10.5 + / iOS.

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

Questo costrutto viene utilizzato per enumerare gli oggetti in una raccolta conforme al NSFastEnumerationprotocollo. Questo approccio ha un vantaggio in termini di velocità perché memorizza i puntatori a più oggetti (ottenuti tramite una singola chiamata di metodo) in un buffer e scorre attraverso di essi avanzando attraverso il buffer usando l'aritmetica del puntatore. Questo è molto più veloce che chiamare -objectAtIndex:ogni volta attraverso il loop.

Vale anche la pena notare che, sebbene tecnicamente sia possibile utilizzare un ciclo for-in per scorrere un NSEnumerator, ho scoperto che questo annulla praticamente tutto il vantaggio di velocità dell'enumerazione rapida. Il motivo è che l' NSEnumeratorimplementazione predefinita -countByEnumeratingWithState:objects:count:posiziona un solo oggetto nel buffer per ogni chiamata.

Ho segnalato questo in radar://6296108(L'enumerazione rapida di NSEnumerators è lenta) ma è stata restituita come Da non correggere. Il motivo è che l'enumerazione rapida precarica un gruppo di oggetti e se si desidera enumerare solo un determinato punto nell'enumeratore (ad es. Fino a quando non viene trovato un oggetto particolare o quando viene soddisfatta la condizione) e utilizzare lo stesso enumeratore dopo lo scoppio del ciclo, spesso accade che diversi oggetti vengano saltati.

Se stai codificando per OS X 10.6 / iOS 4.0 e versioni successive, hai anche la possibilità di utilizzare API basate su blocchi per enumerare matrici e altre raccolte:

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

Puoi anche usare -enumerateObjectsWithOptions:usingBlock:e passare NSEnumerationConcurrente / o NSEnumerationReversecome argomento delle opzioni.


10.4 o precedenti

Il linguaggio standard per pre-10.5 è usare un NSEnumeratorciclo while e, in questo modo:

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

Consiglio di mantenerlo semplice. Legarsi a un tipo di array non è flessibile e il presunto aumento della velocità dell'uso -objectAtIndex:è insignificante per il miglioramento con una rapida enumerazione su 10.5+. (L'enumerazione rapida in realtà utilizza l'aritmetica del puntatore sulla struttura dei dati sottostante e rimuove la maggior parte dell'overhead della chiamata del metodo.) L'ottimizzazione precoce non è mai una buona idea - si traduce in un codice più disordinato per risolvere un problema che non è comunque il collo di bottiglia.

Quando si utilizza -objectEnumerator, si passa facilmente a un'altra raccolta enumerabile (come an NSSet, keys in an NSDictionary, ecc.) Oppure si passa -reverseObjectEnumeratora enumerare un array all'indietro, il tutto senza altre modifiche al codice. Se il codice di iterazione è in un metodo, potresti persino passare qualsiasi NSEnumeratore il codice non deve nemmeno preoccuparsi di ciò che sta itando. Inoltre, un NSEnumerator(almeno quelli forniti dal codice Apple) conserva la raccolta che sta enumerando fintanto che ci sono più oggetti, quindi non devi preoccuparti per quanto tempo rimarrà un oggetto rilasciato automaticamente.

Forse la cosa più grande da cui una NSEnumerator(o enumerazione rapida) ti protegge è avere una raccolta mutabile (array o altro) sotto di te a tua insaputa mentre la stai enumerando. Se accedi agli oggetti per indice, puoi imbatterti in strane eccezioni o errori off-by-one (spesso molto tempo dopo che si è verificato il problema) che possono essere orribili per il debug. L'enumerazione che utilizza uno degli idiomi standard ha un comportamento "fail-fast", quindi il problema (causato da un codice errato) si manifesterà immediatamente quando si tenta di accedere all'oggetto successivo dopo che si è verificata la mutazione. Man mano che i programmi diventano più complessi e multi-thread o dipendono anche da qualcosa che il codice di terze parti può modificare, il fragile codice di enumerazione diventa sempre più problematico. Incapsulamento e astrazione FTW! :-)



28
Nota: la maggior parte dei compilatori emetterà un avviso su "while (object = [e nextObject])". In questo caso, in realtà DEVI usare = invece di ==. Per sopprimere gli avvisi, è possibile aggiungere parentesi aggiuntive: "while ((object = [e nextObject]))".
Adam Rosenfield,

Un'altra cosa da ricordare con NSEnumerator è che utilizzerà più memoria (crea una copia dell'array) rispetto all'approccio objectWithIndex.
diederikh

@QuinnTaylor Usando for (id object in array), c'è un modo per determinare anche l'indice corrente degli oggetti nell'array o è necessario includere un contatore separato?
Coderama,

1
Avresti bisogno di un contatore separato, ma suggerirei di prendere in considerazione l'enumerazione basata su blocchi (che include l'indice) se è disponibile per te.
Quinn Taylor,

Sono strano, ma uso un forciclo come questo:for(;;) { id object = [ e nextObject ] ; if ( !e ) { break ; } ... your loop operation ... }
nielsbot

125

Per OS X 10.4.x e precedenti:

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

Per OS X 10.5.x (o iPhone) e oltre:

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

2
Nel primo esempio, non è nemmeno necessario dichiarare int al di fuori del ciclo. Funziona altrettanto bene e scopre bene la variabile in modo da poterla riutilizzare in un secondo momento se necessario: for (int i = 0; i <[myArray count]; i ++) ... Ma tieni anche presente che chiamando -count ogni volta attraverso l'array è possibile annullare il vantaggio dell'uso di -objectAtIndex:
Quinn Taylor,

1
E in realtà, se stai enumerando una raccolta mutabile, è tecnicamente più corretto controllare il conteggio ogni volta attraverso il ciclo. Ho chiarito la mia risposta per spiegare che l'uso di NSEnumerator o NSFastEnumeration può proteggere dalle mutazioni simultanee dell'array.
Quinn Taylor,

Il contratto for (int i = 0; ...) è un dialetto in linguaggio C (C99 è credo), che io stesso uso, ma non ero sicuro che fosse il valore predefinito XCode.
diederikh

C99 è il valore predefinito tramite Xcode 3.1.x - in alcuni punti futuri il valore predefinito cambierà in GNU99, che (tra le altre cose) supporta le unioni e le strutture anonime. Dovrebbe essere carino ...
Quinn Taylor,

2
L'utilizzo for (NSUInteger i = 0, count = [myArray count]; i < count; i++)è probabilmente il più efficiente e conciso per questo approccio.
Quinn Taylor,

17

I risultati del test e del codice sorgente sono riportati di seguito (è possibile impostare il numero di iterazioni nell'app). Il tempo è in millisecondi e ogni voce è il risultato medio dell'esecuzione del test 5-10 volte. Ho scoperto che in genere è preciso a 2-3 cifre significative e che successivamente varierebbe ad ogni corsa. Ciò fornisce un margine di errore inferiore all'1%. Il test era in esecuzione su un iPhone 3G in quanto questa era la piattaforma di destinazione a cui ero interessato.

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

Le classi fornite da Cocoa per la gestione dei set di dati (NSDictionary, NSArray, NSSet ecc.) Forniscono un'interfaccia molto piacevole per la gestione delle informazioni, senza doversi preoccupare della burocrazia della gestione della memoria, della riallocazione ecc. Naturalmente questo ha un costo però . Penso che sia abbastanza ovvio che dire che usare un array NS di NSNumbers sarà più lento di un array C di float per semplici iterazioni, quindi ho deciso di fare alcuni test e i risultati sono stati piuttosto scioccanti! Non mi aspettavo che fosse così male. Nota: questi test sono condotti su un iPhone 3G in quanto questa è la piattaforma target a cui ero interessato.

In questo test faccio un confronto delle prestazioni di accesso casuale molto semplice tra un float C * e NSArray di NSNumbers

Creo un semplice ciclo per riassumere i contenuti di ciascun array e cronometrarli usando mach_absolute_time (). NSMutableArray impiega in media 400 volte di più !! (non il 400 percento, solo 400 volte più a lungo! quello è il 40.000% in più!).

Intestazione:

// Array_Speed_TestViewController.h

// Test di velocità di array

// Creato da Mehmet Akten il 05/02/2009.

// Copyright MSA Visuals Ltd. 2009. Tutti i diritti riservati.

#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

Implementazione:

// Array_Speed_TestViewController.m

// Test di velocità di array

// Creato da Mehmet Akten il 05/02/2009.

// Copyright MSA Visuals Ltd. 2009. Tutti i diritti riservati.

    #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

Da: memo.tv

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

Disponibile dall'introduzione di blocchi, questo consente di iterare un array con blocchi. La sua sintassi non è bella come l'enumerazione rapida, ma c'è una caratteristica molto interessante: l'enumerazione simultanea. Se l'ordine di enumerazione non è importante e i lavori possono essere eseguiti in parallelo senza blocco, ciò può fornire una notevole velocità su un sistema multi-core. Maggiori informazioni nella sezione di enumerazione simultanea.

[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

L'idea alla base dell'enumerazione rapida è quella di utilizzare l'accesso rapido all'array C per ottimizzare l'iterazione. Non solo dovrebbe essere più veloce del tradizionale NSEnumerator, ma Objective-C 2.0 fornisce anche una sintassi molto concisa.

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

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

NSEnumerator

Questa è una forma di iterazione esterna: [myArray objectEnumerator] restituisce un oggetto. Questo oggetto ha un metodo nextObject che possiamo chiamare in un ciclo fino a quando non restituisce zero

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

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

objectAtIndex: enumerazione

L'utilizzo di un ciclo for che aumenta un numero intero e l'esecuzione di query sull'oggetto mediante [myArray objectAtIndex: index] è la forma più semplice di enumerazione.

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

////////////// Da: darkdust.net


12

I tre modi sono:

        //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. È il modo più veloce in esecuzione e 3. con il completamento automatico dimentica di scrivere la busta di iterazione.

7

Aggiungi eachmetodo nel tuo NSArray category, ne avrai bisogno molto

Codice preso da ObjectiveSugar

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

5

Ecco come dichiarare un array di stringhe e iterare su di esse:

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

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

0

Per 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
}

-1

Fai questo :-

for (id object in array) 
{
        // statement
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.