Estoy buscando el idioma estándar para iterar sobre un NSArray. Mi código debe ser adecuado para OS X 10.4+.
Estoy buscando el idioma estándar para iterar sobre un NSArray. Mi código debe ser adecuado para OS X 10.4+.
Respuestas:
El código generalmente preferido para 10.5 + / iOS.
for (id object in array) {
// do something with object
}
Esta construcción se utiliza para enumerar objetos en una colección que se ajusta al NSFastEnumeration
protocolo. Este enfoque tiene una ventaja de velocidad porque almacena punteros a varios objetos (obtenidos mediante una sola llamada a un método) en un búfer y los itera avanzando a través del búfer utilizando la aritmética del puntero. Esto es mucho más rápido que llamar -objectAtIndex:
cada vez a través del ciclo.
También vale la pena señalar que, si bien técnicamente puede usar un bucle for-in para recorrer un paso NSEnumerator
, he descubierto que esto anula prácticamente todas las ventajas de velocidad de la enumeración rápida. La razón es que la NSEnumerator
implementación predeterminada de -countByEnumeratingWithState:objects:count:
coloca solo un objeto en el búfer en cada llamada.
radar://6296108
Informé esto en (La enumeración rápida de NSEnumerators es lenta) pero se devolvió como No se debe corregir. La razón es que la enumeración rápida busca previamente un grupo de objetos, y si desea enumerar solo en un punto dado en el enumerador (por ejemplo, hasta que se encuentre un objeto en particular o se cumpla una condición) y use el mismo enumerador después de dividir del bucle, a menudo sería el caso de que se omitieran varios objetos.
Si está codificando para OS X 10.6 / iOS 4.0 y superior, también tiene la opción de usar API basadas en bloques para enumerar matrices y otras colecciones:
[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
// do something with object
}];
También puede usar -enumerateObjectsWithOptions:usingBlock:
y pasar NSEnumerationConcurrent
y / o NSEnumerationReverse
como argumento de opciones.
El idioma estándar para pre-10.5 es usar un NSEnumerator
bucle while y un while, así:
NSEnumerator *e = [array objectEnumerator];
id object;
while (object = [e nextObject]) {
// do something with object
}
Recomiendo que sea simple. Atarse a un tipo de matriz es inflexible, y el supuesto aumento de velocidad de uso -objectAtIndex:
es insignificante para la mejora con una enumeración rápida en 10.5+ de todos modos. (La enumeración rápida en realidad usa la aritmética de puntero en la estructura de datos subyacente, y elimina la mayor parte de la sobrecarga de llamadas al método). La optimización prematura nunca es una buena idea: resulta en un código más desordenado para resolver un problema que de todos modos no es su cuello de botella.
Al usarlo -objectEnumerator
, puede cambiar fácilmente a otra colección enumerable (como an NSSet
, keys in an NSDictionary
, etc.), o incluso cambiar -reverseObjectEnumerator
a enumerar una matriz al revés, todo sin otros cambios de código. Si el código de iteración está en un método, incluso podría pasar cualquiera NSEnumerator
y el código ni siquiera tiene que preocuparse por lo que está iterando. Además, un NSEnumerator
(al menos los proporcionados por el código de Apple) retiene la colección que está enumerando siempre que haya más objetos, por lo que no tiene que preocuparse por cuánto tiempo existirá un objeto lanzado automáticamente.
Quizás lo más importante de lo que una NSEnumerator
(o enumeración rápida) lo protege es tener un cambio de colección mutable (matriz o de otro tipo) debajo de usted sin su conocimiento mientras lo enumera. Si accede a los objetos por índice, puede encontrarse con extrañas excepciones o errores ocasionales (a menudo mucho después de que se haya producido el problema) que pueden ser terribles de depurar. La enumeración que usa uno de los modismos estándar tiene un comportamiento de "falla rápida", por lo que el problema (causado por un código incorrecto) se manifestará de inmediato cuando intente acceder al siguiente objeto después de que se haya producido la mutación. A medida que los programas se vuelven más complejos y multiproceso, o incluso dependen de algo que el código de terceros puede modificar, el código de enumeración frágil se vuelve cada vez más problemático. Encapsulación y abstracción FTW! :-)
for (id object in array)
, ¿hay alguna manera de determinar también el índice actual de los objetos en la matriz, o necesita un contador separado?
for
bucle como este:for(;;) { id object = [ e nextObject ] ; if ( !e ) { break ; } ... your loop operation ... }
Para OS X 10.4.xy versiones anteriores:
int i;
for (i = 0; i < [myArray count]; i++) {
id myArrayElement = [myArray objectAtIndex:i];
...do something useful with myArrayElement
}
Para OS X 10.5.x (o iPhone) y más allá:
for (id myArrayElement in myArray) {
...do something useful with myArrayElement
}
for (NSUInteger i = 0, count = [myArray count]; i < count; i++)
es probablemente el más eficiente y conciso que obtendrá para este enfoque.
Los resultados de la prueba y el código fuente están a continuación (puede establecer el número de iteraciones en la aplicación). El tiempo es en milisegundos, y cada entrada es un resultado promedio de ejecutar la prueba 5-10 veces. Descubrí que, en general, tiene una precisión de 2-3 dígitos significativos y después de eso variaría con cada ejecución. Eso da un margen de error de menos del 1%. La prueba se ejecutaba en un iPhone 3G, ya que esa era la plataforma de destino que me interesaba.
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
Las clases proporcionadas por Cocoa para manejar conjuntos de datos (NSDictionary, NSArray, NSSet, etc.) proporcionan una interfaz muy agradable para administrar la información, sin tener que preocuparse por la burocracia de la administración de la memoria, la reasignación, etc. Sin embargo, esto tiene un costo. . Creo que es bastante obvio que decir que usar un NSArray of NSNumbers será más lento que un C Array de flotantes para iteraciones simples, así que decidí hacer algunas pruebas, ¡y los resultados fueron bastante impactantes! No esperaba que fuera tan malo. Nota: estas pruebas se realizan en un iPhone 3G, ya que esa es la plataforma de destino que me interesaba.
En esta prueba, hago una comparación de rendimiento de acceso aleatorio muy simple entre un flotante C * y NSArray of NSNumbers
Creo un bucle simple para resumir el contenido de cada matriz y cronometrarlos usando mach_absolute_time (). ¡El NSMutableArray tarda en promedio 400 veces más! (¡no 400 por ciento, solo 400 veces más! ¡eso es 40,000% más!).
Encabezamiento:
// Array_Speed_TestViewController.h
// Prueba de velocidad de matriz
// Creado por Mehmet Akten el 05/02/2009.
// Copyright MSA Visuals Ltd. 2009. Todos los derechos reservados.
#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
Implementación:
// Array_Speed_TestViewController.m
// Prueba de velocidad de matriz
// Creado por Mehmet Akten el 05/02/2009.
// Copyright MSA Visuals Ltd. 2009. Todos los derechos reservados.
#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
De: memo.tv
////////////////////
Disponible desde la introducción de bloques, esto permite iterar una matriz con bloques. Su sintaxis no es tan buena como la enumeración rápida, pero hay una característica muy interesante: la enumeración concurrente. Si el orden de enumeración no es importante y los trabajos se pueden realizar en paralelo sin bloqueo, esto puede proporcionar una aceleración considerable en un sistema de múltiples núcleos. Más sobre eso en la sección de enumeración concurrente.
[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
La idea detrás de la enumeración rápida es utilizar el acceso rápido a la matriz C para optimizar la iteración. No solo se supone que es más rápido que el NSEnumerator tradicional, sino que Objective-C 2.0 también proporciona una sintaxis muy concisa.
id object;
for (object in myArray) {
[self doSomethingWith:object];
}
/////////////////
NSEnumerator
Esta es una forma de iteración externa: [myArray objectEnumerator] devuelve un objeto. Este objeto tiene un método nextObject que podemos llamar en un bucle hasta que devuelva nil
NSEnumerator *enumerator = [myArray objectEnumerator];
id object;
while (object = [enumerator nextObject]) {
[self doSomethingWith:object];
}
/////////////////
objectAtIndex: enumeración
Usar un bucle for que aumenta un número entero y consultar el objeto usando [myArray objectAtIndex: index] es la forma más básica de enumeración.
NSUInteger count = [myArray count];
for (NSUInteger index = 0; index < count ; index++) {
[self doSomethingWith:[myArray objectAtIndex:index]];
}
////////////// De: darkdust.net
Las tres formas son:
//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
}];
Agregue each
método en su NSArray category
, lo necesitará mucho
Código tomado de ObjectiveSugar
- (void)each:(void (^)(id object))block {
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
block(obj);
}];
}
Para 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
}
Hacer esto :-
for (id object in array)
{
// statement
}