Ich bin auf der Suche nach der Standardsprache, um einen NSArray zu durchlaufen. Mein Code muss für OS X 10.4+ geeignet sein.
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.
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! :-)
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
}
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
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
}];
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);
}];
}
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);
}
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
}