Ich suche nach der Standardsprache, um über ein NSArray zu iterieren. Mein Code muss für OS X 10.4+ geeignet sein.
Ich suche nach der Standardsprache, um über ein NSArray zu iterieren. Mein Code muss für OS X 10.4+ geeignet sein.
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 aufzulisten, die dem NSFastEnumeration
Protokoll entspricht. Dieser Ansatz hat einen Geschwindigkeitsvorteil, da er Zeiger auf mehrere Objekte (die über einen einzelnen Methodenaufruf erhalten wurden) in einem Puffer speichert und diese durchläuft, indem er mithilfe der Zeigerarithmetik durch den Puffer fährt. Dies ist viel schneller als -objectAtIndex:
jedes Mal durch die Schleife aufzurufen .
Es ist auch erwähnenswert, dass Sie zwar technisch gesehen eine For-In-Schleife verwenden können, um eine zu durchlaufen NSEnumerator
, ich jedoch festgestellt habe, dass dies praktisch den gesamten Geschwindigkeitsvorteil einer schnellen Aufzählung zunichte macht. Der Grund dafür ist , dass der Standard NSEnumerator
Implementierung von -countByEnumeratingWithState:objects:count:
Orten nur ein Objekt in dem Puffer bei jedem Aufruf.
Ich habe dies in radar://6296108
(Die schnelle Aufzählung von NSEnumerators ist träge) gemeldet, aber es wurde als Nicht zu beheben zurückgegeben. Der Grund dafür ist, dass eine schnelle Aufzählung eine Gruppe von Objekten vorab abruft und Sie nur bis zu einem bestimmten Punkt im Aufzähler aufzählen möchten (z. B. bis ein bestimmtes Objekt gefunden wurde oder die Bedingung erfüllt ist) und nach dem Ausbruch denselben Aufzähler verwenden In der Schleife würden häufig mehrere Objekte übersprungen.
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:
und NSEnumerationConcurrent
und oder oder NSEnumerationReverse
als Optionsargument verwenden und ü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. Es ist unflexibel, sich an einen Array-Typ zu binden, und die angebliche Geschwindigkeitssteigerung bei der Verwendung -objectAtIndex:
ist ohnehin unbedeutend für die Verbesserung bei schneller Aufzählung auf 10.5+. (Bei der schnellen Aufzählung wird tatsächlich eine Zeigerarithmetik für die zugrunde liegende Datenstruktur verwendet, und der größte Teil des Methodenaufrufaufwands entfällt.) Eine vorzeitige Optimierung ist niemals eine gute Idee - sie führt zu unübersichtlichem Code, um ein Problem zu lösen, das ohnehin nicht Ihr Engpass ist.
Bei der Verwendung -objectEnumerator
können Sie ganz einfach zu einer anderen aufzählbaren Sammlung wechseln (z. B. zu NSSet
, Schlüssel in einer NSDictionary
usw.) oder sogar zu wechseln, -reverseObjectEnumerator
um ein Array rückwärts aufzulisten, ohne dass andere Codeänderungen erforderlich sind. Wenn sich der Iterationscode in einer Methode befindet, können Sie sogar eine übergeben, NSEnumerator
und der Code muss sich nicht einmal darum kümmern, was er iteriert. Außerdem NSEnumerator
behält ein (zumindest die vom Apple-Code bereitgestellten) die aufgezählte Sammlung bei, solange mehr Objekte vorhanden sind, sodass Sie sich keine Gedanken darüber machen müssen, wie lange ein automatisch freigegebenes Objekt vorhanden sein wird.
Vielleicht ist das Größte, wovor Sie eine NSEnumerator
(oder schnelle Aufzählung) schützt, dass sich eine veränderbare Sammlung (Array oder andere) ohne Ihr Wissen unter Ihnen ändert, während Sie sie aufzählen. Wenn Sie über den Index auf die Objekte zugreifen, können seltsame Ausnahmen oder Fehler auftreten (häufig lange nach dem Auftreten des Problems), deren Debugging schrecklich sein kann. Die Aufzählung mit einer der Standard-Redewendungen hat ein "Fail-Fast" -Verhalten, sodass sich das Problem (verursacht durch falschen Code) sofort manifestiert, wenn Sie versuchen, auf das nächste Objekt zuzugreifen, nachdem die Mutation aufgetreten ist. Wenn Programme komplexer und multithreaded werden oder sogar von etwas abhängen, das durch Code von Drittanbietern geändert werden kann, wird fragiler Aufzählungscode zunehmend problematisch. Verkapselung und Abstraktion FTW! :-)
for (id object in array)
es eine Möglichkeit, auch den aktuellen Index des Objekts im Array zu bestimmen, oder muss ein separater Zähler eingefügt werden?
for
Schleife wie diese:for(;;) { id object = [ e nextObject ] ; if ( !e ) { break ; } ... your loop operation ... }
Für OS X 10.4.x und frühere Versionen:
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
}
for (NSUInteger i = 0, count = [myArray count]; i < count; i++)
ist wahrscheinlich die effizienteste und prägnanteste, die Sie für diesen Ansatz erhalten.
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 angegeben, und jeder Eintrag ist ein durchschnittliches Ergebnis der fünf- bis zehnmaligen Ausführung des Tests. Ich fand heraus, dass es im Allgemeinen auf 2-3 signifikante Stellen genau ist und danach mit jedem Lauf variieren würde. Dies ergibt eine Fehlerquote 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 bereitgestellten Klassen für die Verarbeitung von Datensätzen (NSDictionary, NSArray, NSSet usw.) bieten eine sehr schöne Schnittstelle für die Verwaltung von Informationen, ohne sich um die Bürokratie der Speicherverwaltung, Neuzuweisung usw. kümmern zu müssen. Dies ist natürlich mit Kosten verbunden . Ich denke, es ist ziemlich offensichtlich, dass die Verwendung eines NSArray von NSNumbers für einfache Iterationen langsamer sein wird als die Verwendung eines C-Arrays von Floats. Deshalb habe ich beschlossen, einige Tests durchzuführen, und die Ergebnisse waren ziemlich schockierend! Ich hatte nicht erwartet, dass es so schlimm wird. 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 sehr einfachen Vergleich der Leistung bei wahlfreiem Zugriff zwischen einem C-Float * und einem NSArray von NSNumbers durch
Ich erstelle eine einfache Schleife, um den Inhalt jedes Arrays zusammenzufassen und mit mach_absolute_time () zu messen. Das NSMutableArray dauert 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 05/02/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 05/02/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.
///////////////////////
Dies ist seit der Einführung von Blöcken verfügbar und ermöglicht das Iterieren eines Arrays mit Blöcken. Die Syntax ist nicht so gut 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 ohne Sperren parallel ausgeführt werden können, kann dies auf einem Mehrkernsystem zu einer erheblichen Beschleunigung 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 besteht darin, den schnellen C-Array-Zugriff zu verwenden, um die Iteration zu optimieren. Es soll nicht nur schneller als der herkömmliche 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 Verwendung einer for-Schleife, die eine Ganzzahl erhöht, und das Abfragen 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]];
}
/////////////// Von: 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üge eine each
Methode hinzu NSArray category
, du wirst sie sehr brauchen
Code aus ObjectiveSugar
- (void)each:(void (^)(id object))block {
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
block(obj);
}];
}
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
}
Mach das :-
for (id object in array)
{
// statement
}