¿Cómo itero sobre un NSArray?

Resuelto Steve McLeod asked hace 15 años • 8 respuestas

Estoy buscando el idioma estándar para iterar sobre un archivo NSArray. Mi código debe ser adecuado para OS X 10.4+.

Steve McLeod avatar Jun 14 '09 21:06 Steve McLeod
Aceptado

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 cumple con el NSFastEnumerationprotocolo. Este enfoque tiene una ventaja de velocidad porque almacena punteros a varios objetos (obtenidos mediante una única llamada a un método) en un búfer y los recorre en iteración avanzando a través del búfer usando aritmética de punteros. Esto es mucho más rápido que llamar -objectAtIndex:cada vez a través del bucle.

También vale la pena señalar que, si bien técnicamente puedes usar un bucle for-in para recorrer un NSEnumerator, descubrí que esto anula prácticamente toda la ventaja de velocidad de la enumeración rápida. La razón es que la NSEnumeratorimplementación predeterminada -countByEnumeratingWithState:objects:count:coloca solo un objeto en el búfer en cada llamada.

Informé esto en radar://6296108(La enumeración rápida de NSEnumerators es lenta) pero se devolvió como No debe solucionarse. La razón es que la enumeración rápida busca previamente un grupo de objetos, y si desea enumerar solo hasta un punto determinado 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 dividirlo del bucle, a menudo se daba 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 utilizar 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 NSEnumerationConcurrenty/o NSEnumerationReversecomo argumento de opciones.


10.4 o anterior

El lenguaje estándar para versiones anteriores a 10.5 es usar un NSEnumeratorbucle y while, así:

NSEnumerator *e = [array objectEnumerator];
id object;
while (object = [e nextObject]) {
  // do something with object
}

Recomiendo mantenerlo simple. Vincularse a un tipo de matriz es inflexible y, de -objectAtIndex:todos modos, el supuesto aumento de velocidad de uso es insignificante para la mejora con la enumeración rápida en 10.5+. (La enumeración rápida en realidad utiliza aritmética de punteros 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; da como resultado un código más desordenado para resolver un problema que de todos modos no es su cuello de botella.

Al usar -objectEnumerator, puedes cambiar muy fácilmente a otra colección enumerable (como an NSSet, claves en an NSDictionary, etc.), o incluso cambiar a -reverseObjectEnumeratorenumerar 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 NSEnumeratory 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) conserva la colección que enumera siempre que haya más objetos, por lo que no tiene que preocuparse por cuánto tiempo existirá un objeto liberado automáticamente.

Quizás lo más importante de lo que NSEnumeratorlo protege una enumeración rápida es que una colección mutable (matriz o de otro tipo) cambie debajo de usted sin su conocimiento mientras la enumera. Si accede a los objetos por índice, puede encontrarse con excepciones extrañas o errores uno por uno (a menudo mucho después de que se haya producido el problema) cuya depuración puede ser horrible. La enumeración utilizando 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á 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 multiproceso, o incluso dependen de algo que el código de terceros pueda modificar, el código de enumeración frágil se vuelve cada vez más problemático. ¡Encapsulación y abstracción FTW! :-)


Quinn Taylor avatar Jun 14 '2009 14:06 Quinn Taylor

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 posteriores:

for (id myArrayElement in myArray) {
   ...do something useful with myArrayElement
}
diederikh avatar Jun 14 '2009 14:06 diederikh