¿Cómo itero sobre un NSArray?
Estoy buscando el idioma estándar para iterar sobre un NSArray. Mi código debe ser adecuado para OS X 10.4+.
7 answers
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 a la NSFastEnumeration
protocolo. Este enfoque tiene una ventaja de velocidad porque almacena punteros a varios objetos (obtenidos a través de una sola llamada al método) en un búfer e itera a través de ellos avanzando a través del búfer usando aritmética de puntero. Esto es mucho más rápido que llamar -objectAtIndex:
cada vez a través del bucle.
También es vale la pena señalar que si bien técnicamente puede usar un bucle for-in para pasar por un NSEnumerator
, he encontrado que esto anula prácticamente toda la ventaja de velocidad de la enumeración rápida. La razón es que la implementación predeterminada NSEnumerator
de -countByEnumeratingWithState:objects:count:
coloca solo un objeto en el búfer en cada llamada.
Informé de esto en radar://6296108
(La enumeración rápida de NSEnumerators es lenta), pero se devolvió como Si No Se Arreglara. La razón es que la enumeración rápida obtiene previamente un grupo de objetos, y si desea enumerar solo un punto dado en el enumerador (por ejemplo, hasta que se encuentre un objeto en particular, o se cumpla una condición) y usar el mismo enumerador después de salir 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 versiones posteriores, 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 el argumento de opciones.
10.4 o anterior
El modismo estándar para pre-10.5 es usar un NSEnumerator
y un bucle while, así:
NSEnumerator *e = [array objectEnumerator];
id object;
while (object = [e nextObject]) {
// do something with object
}
Recomiendo mantenerlo simple. Atarse a un tipo de matriz es inflexible, y el supuesto aumento de velocidad de usar -objectAtIndex:
es insignificante para la mejora con enumeración rápida en 10.5+ de todos modos. (La enumeración rápida en realidad usa aritmética de puntero en la estructura de datos subyacente y elimina la mayor parte de la sobrecarga de la llamada al método.) La optimización prematura nunca es una buena idea: da como resultado que el código messier resuelva un problema que no es su cuello de botella de todos modos.
Cuando se usa -objectEnumerator
, usted cambia muy fácilmente a otra colección enumerable (como un NSSet
, claves en un NSDictionary
, etc.), o incluso cambiar a -reverseObjectEnumerator
para enumerar una matriz hacia atrás, todo sin otros cambios de código. Si el código de iteración está en un método, incluso podría pasar cualquier NSEnumerator
y el código ni siquiera tiene que preocuparse qué está iterando. Además, un NSEnumerator
(al menos los proporcionados por el código de Apple) conserva la colección que está enumerando mientras haya más objetos, por lo que no tiene que preocuparse por cuánto tiempo existirá un objeto autoreleased.
Tal vez lo más grande de lo que un NSEnumerator
(o enumeración rápida) te protege es tener una colección mutable (matriz o de otra manera) cambiar debajo de ti sin tu conocimiento mientras lo estás enumerando. Si accede a los objetos por index, puede encontrarse con extrañas excepciones o errores desactivados por uno (a menudo mucho después de que se haya producido el problema) que pueden ser horribles de depurar. La enumeración usando uno de los modismos estándar tiene un comportamiento "fail-fast", por lo que el problema (causado por un código incorrecto) se manifestará inmediatamente 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 multihilo, 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! :-)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-20 21:48:04
Para OS X 10.4.x y 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
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-02 15:17:46
Los resultados de la prueba y el código fuente se encuentran 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 generalmente es precisa a 2-3 dígitos significativos y después de eso variaría con cada carrera. Esto da un margen de error inferior al 1%. La prueba se estaba ejecutando en un iPhone 3G ya que esa es la plataforma de destino en la que estaba interesado.
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 el Cacao para manejo de conjuntos de datos (NSDictionary, NSArray, NSSet, etc.) proporcionar una interfaz muy agradable para la gestión de la información, sin tener que preocuparse por la burocracia de la gestión de la memoria, la reasignación, etc. Sin embargo, por supuesto, esto tiene un costo. Creo que es bastante obvio que decir usando un NSArray de NSNumbers va a ser más lento que una matriz C de flotadores para iteraciones simples, así que decidí hacer algunas pruebas, y los resultados fueron bastante impactantes! No esperaba que fuera tan malo. Nota: estos las pruebas se llevan a cabo en un iPhone 3G ya que esa es la plataforma de destino en la que estaba interesado.
En esta prueba hago una comparación de rendimiento de acceso aleatorio muy simple entre un flotador C* y NSArray de NSNumbers
Creo un bucle simple para resumir el contenido de cada array 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!).
Cabecera:
// 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
Aplicació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
Desde : memo.tv
////////////////////
Disponible desde la introducción de bloques, esto permite iterar una matriz con bloques. Su sintaxis no es tan agradable 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 hacer en paralelo sin bloqueo, esto puede proporcionar una aceleración considerable en un sistema multinúcleo. Más sobre eso en la enumeración concurrente apartado.
[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 usar 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];
}
/////////////////
NSEnumerador
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 devuelve nil
NSEnumerator *enumerator = [myArray objectEnumerator];
id object;
while (object = [enumerator nextObject]) {
[self doSomethingWith:object];
}
/////////////////
ObjectAtIndex: enumeration
Usar un bucle for que aumenta un 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-11 15:30:38
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
}];
- Es la forma más rápida de ejecución, y 3. con autocompletado olvídate de escribir sobre iteración.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-31 07:11:20
Agregar each
método en su NSArray category
, vas a necesitarlo mucho
Código tomado de ObjectiveSugar
- (void)each:(void (^)(id object))block {
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
block(obj);
}];
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-01 17:14:21
Así es como se declara una matriz de cadenas y se itera sobre ellas:
NSArray *langs = @[@"es", @"en", @"pt", @"it", @"fr"];
for (int i = 0; i < [langs count]; i++) {
NSString *lang = (NSString*) [langs objectAtIndex:i];
NSLog(@"%@, ",lang);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-20 16:54:58