Sto cercando il linguaggio standard per iterare su un NSArray. Il mio codice deve essere adatto per OS X 10.4+.
Sto cercando il linguaggio standard per iterare su un NSArray. Il mio codice deve essere adatto per OS X 10.4+.
Risposte:
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 NSFastEnumeration
protocollo. 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' NSEnumerator
implementazione 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 NSEnumerationConcurrent
e / o NSEnumerationReverse
come argomento delle opzioni.
Il linguaggio standard per pre-10.5 è usare un NSEnumerator
ciclo 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 -reverseObjectEnumerator
a enumerare un array all'indietro, il tutto senza altre modifiche al codice. Se il codice di iterazione è in un metodo, potresti persino passare qualsiasi NSEnumerator
e 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! :-)
for (id object in array)
, c'è un modo per determinare anche l'indice corrente degli oggetti nell'array o è necessario includere un contatore separato?
for
ciclo come questo:for(;;) { id object = [ e nextObject ] ; if ( !e ) { break ; } ... your loop operation ... }
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
}
for (NSUInteger i = 0, count = [myArray count]; i < count; i++)
è probabilmente il più efficiente e conciso per questo approccio.
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
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
}];
Aggiungi each
metodo 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);
}];
}
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
}
Fai questo :-
for (id object in array)
{
// statement
}