bloques y ARC: copie o bloquee con la compilation de versión (causada por el nivel de optimization)

Estoy usando Xcode 4.3.3 y desarrollando para iOS 5.0+. En el desarrollo de una aplicación ARC iOS, he comenzado a usar bloques como mecanismo de callback para operaciones asíncronas. La aplicación funciona bien en el simulador y en el dispositivo.

Luego lo ejecuté el perfilador por primera vez, y comenzó a chocar conmigo casi de inmediato, en particular, un EXC_BAD_ACCESS al tratar de invocar el primer bloque de callback.

Después de una pequeña investigación, quedó claro que la diferencia de comportamiento se debía a que el perfilador se ejecuta en "Modo de lanzamiento" de manera pnetworkingeterminada, en particular, con el Nivel de optimization establecido en "Más rápido, más pequeño [-Os]" en lugar de "Ninguno [-O0] "

Por ejemplo, el siguiente código (simplificado para esta pregunta) se bloquearía al intentar ejecutar callbackBlock:

- (void) setCallbackBlock:(void (^)(NSString *input))block { callbackBlock = block; } - (void) invokeCallbackWithInput:(NSString *)input { if (callbackBlock) { callbackBlock(input); } } 

Depurando en él, llamando a setCallbackBlock con el nivel de optimization establecido en "Ninguno", el bloque entrante sería un NSStackBlock , y el callbackBlock se convertiría en un NSMallocBlock .

Sin embargo, con el nivel de optimization "más rápido, más pequeño", se mantuvo como NSStackBlock .

Cambiar el código de setter para usar [block copy] corrige el problema de locking (basado en lockings de iOS 5 bloquearse solo con Release Build ).

Sin embargo, otra pregunta relacionada indica que esto no debería ser necesario con ARC – las variables de bloque se copyn en el montón en ARC – ¿Por qué el bloque Objective-C todavía funciona sin copyrlo en el montón?

Entonces, mi pregunta: ¿Qué está pasando aquí y por qué? (Además, ¿cómo pueden ser correctas ambas respuestas …?)

Editar : para aclarar cómo se declara callbackBlock, justo arriba de mi @implementation donde son esos methods es esto:

 @interface MyClass () { void (^callbackBlock)(NSString *input); } @end 

Entonces, mi pregunta: ¿Qué está pasando aquí y por qué? (Además, ¿cómo pueden ser correctas ambas respuestas …?)

De hecho, creo que la respuesta a la otra pregunta es incorrecta, ya que no responde a esa pregunta en particular sobre bloques en ARC. La pregunta es sobre pasar un bloque basado en stack de una function / método a otro. La respuesta es sobre algo diferente, que está capturando __block variables dentro de un bloque. Ese es un problema diferente.

La respuesta a su pregunta está en las preguntas frecuentes de las Notas de la versión de transición a ARC:

Los bloques "solo funcionan" cuando pasas bloques por la stack en modo ARC, como en un retorno. Ya no tiene que llamar a Block Copy. Todavía necesita usar [^ {} copy] al pasar "hacia abajo" la stack en arrayWithObjects: y otros methods que hacen una retención.

Entonces, la forma en que esto funciona es que cuando pasas un bloque (en tu caso, un literal de bloque asignado en la stack), el comstackdor no copy ese bloque cuando inicializa el parámetro para esa llamada. La function o método llamado tiene la responsabilidad de copyr ese bloque si es necesario.

Donde ARC copy bloques automáticamente es cuando devuelve un bloque de una function o método. En ese caso, el comstackdor sabe que debe hacerle una copy al montón, y así lo hace.

Por lo tanto, su organizador debe hacer una copy en bloque, incluso con ARC.

Espero que eso ayude.

Este es un largo comentario sobre la respuesta de Firoze.

El documento Conteo automático de references ", en la sección 7.5, dice:

Con la exception de los __strong realizados como parte de la initialization de una variable de parámetro __strong o leyendo una variable __weak , cada vez que estas semánticas requieren retener un valor de tipo de puntero de bloque, tiene el efecto de una Block_copy de Block_copy . El optimizador puede eliminar esas copys cuando ve que el resultado se usa solo como argumento para una llamada.

Y este es el comportamiento que he visto.

Entonces, si callbackBlock es una variable de instancia fuerte, ARC debería copyr cualquier bloque bloqueado asignado en block . Es decir, la versión de debugging fue correcta y la versión de lanzamiento es un error del comstackdor.

Si esto es correcto, entonces has encontrado un error de comstackdor y debería informarlo. Informar que no sería malo de ninguna manera, debería generar una respuesta definitiva.