2012-01-11 11 views
9

Considere el siguiente código:iOS: Bloque propiedad situada directamente bloquea cuando accede

@interface ClassA : NSObject 
@property (nonatomic, copy) void(^blockCopy)(); 
@end 

@implementation ClassA 

@synthesize blockCopy; 

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

@end 

A continuación, utilizarlo en una clase que tiene un strong propiedad de 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 yo ejecutar ese O3 construido con ARC habilitado, en iOS, se bloquea durante la llamada self.someA.blockCopy(); dentro de objc_retain. ¿Por qué?

Ahora se dan cuenta de que las personas son probablemente va a decir que debería ser la creación con self.blockCopy = inBlock pero yo tipo de ARC piensan que debería estar haciendo lo correcto. Si miro a la asamblea (ARMv7) producido a partir del 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} 

que está llamando objc_storeStrong que a su vez hace un retain en el bloque y una release en el viejo bloque. Supongo que ARC no está notando correctamente que es una propiedad de bloque, ya que creo que debería llamar al objc_retainBlock en lugar del objc_retain normal.

O, ¿estoy completamente equivocado y en realidad ARC está haciendo lo que documenta y acabo de leerlo de la manera incorrecta?

Discusión muy bienvenida sobre esto - Encuentro esto bastante intrigante.

Puntos a tener en cuenta:

  • no choque en OS X.
  • No Crash construido O0.

Respuesta

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

tiene que copiar el bloque ya sea en la asignación o cuando pasó a esta función. Mientras que ARC resuelve el problema de auto-movimiento-al-montón-en-retorno, no lo hace para los argumentos (no puede hacer para la idiosincrasia de C).

Que no se cuelgue en ciertos entornos es meramente casual; no se bloqueará siempre que la versión de pila del bloque no se haya sobrescrito. Una señal segura de esto es cuando se produce un bloqueo que desaparece con la optimización desactivada. Con la optimización desactivada, el compilador no reutilizará la memoria de la pila dentro de un ámbito determinado, lo que hará que la memoria sea "válida" por mucho tiempo después de lo 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 compilador 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 una gran cantidad de estados, incluidos, potencialmente, otros bloques, entonces el Block_copy() podría ser realmente realmenterealmente caro.

I.e.si tuviera algo como:

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

... y que implicaba una Block_copy() por el mero hecho de la cesión, que haría imposible el uso de bloques de forma consistente y sin riesgo de problemas de rendimiento patológicos. Como el compilador no tiene forma de saber si SomeRandomFunc() es síncrono o asíncrono, no hay forma de administrarlo automáticamente (en este momento, estoy seguro de que es deseable deshacerse de este posible cable trampa).

+0

Estoy un poco sorprendido de ver que atraviesa un 'objc_storeStrong' cuando asigna, sin embargo, y no puede" hacer lo correcto ". – mattjgalloway

+0

+1 interesante - ty! – Till

+0

Por lo que entiendo (hace un tiempo), existen casos límite que evitan que el compilador emita código que "simplemente funcione" en todos los casos correctamente. Bajo ARC, la línea dura en la arena requiere que el compilador demuestre exactamente que cualquier patrón de código dado siempre funcionará en todas partes. En este caso, no puede hacer eso porque hay usos válidos para argumentos de bloques solo de pila pasados ​​a través de cualquier sitio de llamadas dado (incluyendo 'objc_storeStrong()'). – bbum

Cuestiones relacionadas