iOS: la propiedad Bloquear directamente se bloquea cuando se accede

Considere el siguiente código:

@interface ClassA : NSObject @property (nonatomic, copy) void(^blockCopy)(); @end @implementation ClassA @synthesize blockCopy; - (void)giveBlock:(void(^)())inBlock { blockCopy = inBlock; } @end 

Luego utilícelo en una class que tiene una propiedad strong del tipo ClassA llamada someA :

 self.someA = [[ClassA alloc] init]; [self.someA giveBlock:^{ NSLog(@"self = %@", self); }]; dispatch_async(dispatch_get_main_queue(), ^{ self.someA.blockCopy(); self.someA = nil; }); 

Si ejecuto ese O3 construido con ARC habilitado, en iOS, se bloquea durante self.someA.blockCopy(); llama dentro de objc_retain ¿Por qué?

Ahora me doy count de que la gente probablemente dirá que debería configurarlo con self.blockCopy = inBlock pero pensé que ARC debería estar haciendo lo correcto aquí. Si miro el ensamblaje (ARMv7) producido desde el método giveBlock: se ve así:

  .align 2 .code 16 .thumb_func "-[ClassA giveBlock:]" "-[ClassA giveBlock:]": push {r7, lr} movw r1, :lower16:(_OBJC_IVAR_$_ClassA.blockCopy-(LPC0_0+4)) mov r7, sp movt r1, :upper16:(_OBJC_IVAR_$_ClassA.blockCopy-(LPC0_0+4)) LPC0_0: add r1, pc ldr r1, [r1] add r0, r1 mov r1, r2 blx _objc_storeStrong pop {r7, pc} 

Eso es llamar objc_storeStrong que a su vez hace una retain en el bloque y una release en el bloque antiguo. Mi conjetura es que ARC no está notando adecuadamente que es una propiedad de bloque, ya que creo que debería llamar a objc_retainBlock lugar de a la objc_retain normal.

O, ¿estoy totalmente equivocado y, de hecho, ARC está haciendo lo que documenta y acabo de leerlo de manera incorrecta?

Discusión muy bienvenido en esto: me parece bastante intrigante.

Puntos a tener en count:

  • No falla en OS X.
  • No se bloquea el O0 construido.

 - (void)giveBlock:(void(^)())inBlock { blockCopy = inBlock; } 

Debe copyr el bloque en la asignación o cuando se pasa a esta function. Mientras ARC resuelve el problema de auto-mover-a-montón-a-retorno, no lo hace para los arguments (no se puede hacer a las idiosincrasias de C).

Que no se cuelgue en ciertos ambientes es simplemente una coincidencia; no se bloqueará mientras no se sobrescriba la versión de stack del bloque. Un signo seguro de esto es cuando tienes un locking que desaparece con la optimization desactivada. Con la optimization desactivada, el comstackdor no reutilizará la memory de la stack dentro de un scope dado, haciendo que la memory sea "válida" durante mucho time después de que debería ser.


Todavía no entiendo por qué no puede hacer un objc_blockRetain en lugar de un objc_retain normal, sin embargo. El comstackdor conoce el tipo después de todo.

Estoy bastante seguro de que el problema es el costo potencial de la tarea. Si el bloque captura un gran estado, incluidos, potencialmente, otros bloques, entonces el Block_copy () podría ser realmente realmente muy costoso.

Es decir, si tuvieras algo como:

 BlockType b = ^(...) { ... capture lots of gunk ... }; SomeRandomFunc(b); 

… y eso implicaba un Block_copy () simplemente por la asignación, haría imposible utilizar bloques de forma coherente sin riesgo de problemas de performance patológico. Debido a que no hay forma de que el comstackdor sepa si SomeRandomFunc() es síncrona o asíncrona, no hay manera de administrar esto automáticamente (en este momento, estoy seguro de que es deseable deshacerse de este potencial cable de connection).