Además de las diferencias obvias:
- Utilizar
enumerateObjectsUsingBlock
cuando necesite tanto el índice como el objeto No lo use(estaba equivocado sobre esto, vea la respuesta de bbum)enumerateObjectsUsingBlock
cuando necesite modificar variables locales
¿ enumerateObjectsUsingBlock
Generalmente se considera mejor o peor cuando for (id obj in myArray)
también funcionaría? ¿Cuáles son las ventajas / desventajas (por ejemplo, es más o menos eficaz)?
objective-c
multithreading
ios4
objective-c-blocks
Paul Wheeler
fuente
fuente
Respuestas:
En última instancia, use el patrón que desee usar y que sea más natural en el contexto.
Si bien
for(... in ...)
es bastante conveniente y sintácticamente breve,enumerateObjectsUsingBlock:
tiene una serie de características que pueden resultar interesantes o no:enumerateObjectsUsingBlock:
será tan rápido o más rápido que la enumeración rápida (for(... in ...)
utiliza elNSFastEnumeration
soporte para implementar la enumeración). La enumeración rápida requiere la traducción de una representación interna a la representación para la enumeración rápida. Hay gastos generales en el mismo. La enumeración basada en bloques permite a la clase de recopilación enumerar contenidos tan rápido como el recorrido más rápido del formato de almacenamiento nativo. Probablemente irrelevante para las matrices, pero puede ser una gran diferencia para los diccionarios."No use enumerateObjectsUsingBlock cuando necesite modificar variables locales" - no es cierto; puedes declarar a tus locales como
__block
y se podrán escribir en el bloque.enumerateObjectsWithOptions:usingBlock:
admite enumeración simultánea o inversa.Con los diccionarios, la enumeración basada en bloques es la única forma de recuperar la clave y el valor simultáneamente.
Personalmente, uso
enumerateObjectsUsingBlock:
más a menudo quefor (... in ...)
, pero, una vez más, elección personal.fuente
enumerateObjectsUsingBlock
que todavía es significativamente más lento que la enumeración rápida para matrices y conjuntos. ¿Me pregunto porque? iosdevelopertips.com/objective-c/…enumerateObjectsUsingBlock
envuelven cada invocación del bloque con un grupo de liberación automática (a partir de OS X 10.10 al menos). Esto explica la diferencia de rendimiento en comparación confor in
lo que no hace eso.Para una enumeración simple, simplemente usando la enumeración rápida (es decir, un
for…in…
bucle) es la opción más idiomática. El método de bloqueo puede ser marginalmente más rápido, pero eso no importa mucho en la mayoría de los casos: pocos programas están vinculados a la CPU, e incluso entonces es raro que el ciclo en sí en lugar del cálculo interno sea un cuello de botella.Un bucle simple también se lee más claramente. Aquí está el repetitivo de las dos versiones:
Incluso si agrega una variable para rastrear el índice, el bucle simple es más fácil de leer.
Entonces, ¿cuándo deberías usar
enumerateObjectsUsingBlock:
? Cuando está almacenando un bloque para ejecutarlo más tarde o en varios lugares. Es bueno para cuando estás usando un bloque como una función de primera clase en lugar de un reemplazo forzado para un cuerpo de bucle.fuente
enumerateObjectsUsingBlock:
será la misma velocidad o más rápido que la enumeración rápida en todos los casos.for(... in ...)
utiliza una enumeración rápida que requiere que la recopilación proporcione alguna representación provisional de las estructuras de datos internas. Como notas, probablemente irrelevante.When you're storing a block to execute later or in multiple places. It's good for when you're actually using a block as a first-class function rather than an overwrought replacement for a loop body.
enumerateObjects...
realidad puede ser más lento que la enumeración rápida con un bucle. Hice esta prueba varios miles de veces; el cuerpo del bloque y bucle eran los mismos de una sola línea de código:[(NSOperation *)obj cancel];
. Los promedios: bucle enum rápido --[JHStatusBar dequeueStatusMessage:] [Line: 147] Fast enumeration time (for..in..loop): 0.000009
y para el bloque --[JHStatusBar dequeueStatusMessage:] [Line: 147] Enumeration time using block: 0.000043
. Es extraño que la diferencia horaria sea tan grande y consistente, pero, obviamente, este es un caso de prueba muy específico.Aunque esta pregunta es antigua, las cosas no han cambiado, la respuesta aceptada es incorrecta.
La
enumerateObjectsUsingBlock
API no estaba destinada a reemplazarfor-in
, sino a un caso de uso totalmente diferente:withOptions:
parámetro)La enumeración rápida
for-in
sigue siendo el método idiomático de enumerar una colección.La enumeración rápida se beneficia de la brevedad del código, la legibilidad y las optimizaciones adicionales que lo hacen anormalmente rápido. ¡Más rápido que un viejo C for-loop!
Una prueba rápida concluye que en el año 2014 en iOS 7,
enumerateObjectsUsingBlock
es consistentemente un 700% más lento que for-in (basado en iteraciones de 1 mm de una matriz de 100 elementos).¿Es el rendimiento una preocupación práctica real aquí?
Definitivamente no, con una rara excepción.
El punto es demostrar que hay poco beneficio al usar
enumerateObjectsUsingBlock:
más defor-in
sin una buena razón. No hace que el código sea más legible ... o más rápido ... o seguro para subprocesos. (Otro concepto erróneo común).La elección se reduce a la preferencia personal. Para mí, la opción idiomática y legible gana. En este caso, se trata de una enumeración rápida
for-in
.Punto de referencia:
Resultados:
fuente
enumerateObjectsUsingBlock
realidad es 5 veces más lento. Aparentemente, esto se debe a un grupo de liberación automática que envuelve cada invocación del bloque, lo que no sucede en elfor in
caso.enumerateObjectsUsingBlock:
sigue siendo 4 veces más lento en el iPhone 6 iOS9 real, usando Xcode 7.x para construir.enumerate
sintaxis porque se siente como FP y no quieren escuchar el análisis de rendimiento.enumerate:
Para responder a la pregunta sobre el rendimiento, realicé algunas pruebas con mi proyecto de prueba de rendimiento . Quería saber cuál de las tres opciones para enviar un mensaje a todos los objetos en una matriz es la más rápida.
Las opciones fueron:
1) makeObjectsPerformSelector
2) enumeración rápida y envío regular de mensajes
3) enumerateObjectsUsingBlock y envío regular de mensajes
Resulta que makeObjectsPerformSelector fue el más lento con diferencia. Tomó el doble de tiempo que la enumeración rápida. Y enumerateObjectsUsingBlock fue el más rápido, fue alrededor del 15-20% más rápido que la iteración rápida.
Entonces, si está muy preocupado por el mejor rendimiento posible, use enumerateObjectsUsingBlock. Pero tenga en cuenta que, en algunos casos, el tiempo que lleva enumerar una colección se ve reducido por el tiempo que lleva ejecutar el código que desea que ejecute cada objeto.
fuente
Es bastante útil usar enumerateObjectsUsingBlock como un bucle externo cuando desea romper bucles anidados.
p.ej
La alternativa es usar declaraciones goto.
fuente
Gracias a @bbum y @Chuck por comenzar comparaciones exhaustivas sobre el rendimiento. Me alegra saber que es trivial. Parece que me he ido con:
for (... in ...)
- Como mi goto predeterminado. Más intuitivo para mí, más historial de programación aquí que cualquier preferencia real: reutilización de idiomas cruzados, menos tipeo para la mayoría de las estructuras de datos debido a IDE autocompletar: P.enumerateObject...
- cuando se necesita acceso al objeto y al índice. Y al acceder a estructuras que no son matrices o de diccionario (preferencia personal)for (int i=idx; i<count; i++)
- para matrices, cuando necesito comenzar en un índice distinto de cerofuente