Pregunta

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 úsalo en una clase que tenga un strong Propiedad del tipo ClassA llamó 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 eso construido O3 con arco habilitado, en iOS, se bloquea durante el self.someA.blockCopy(); llamar objc_retain. ¿Por qué?

Ahora me doy cuenta de que la gente probablemente va a decir 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 por el giveBlock: método 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 esta llamando objc_storeStrong que a su vez hace un retain en el bloque y un release en el viejo bloque. Supongo que ARC no se da cuenta correctamente de que es una propiedad de bloque, ya que creo que debería llamar objc_retainBlock en lugar de lo normal objc_retain.

O, ¿estoy totalmente equivocado y en realidad está haciendo lo que documenta y lo he leído de la manera incorrecta?

Discusión muy bienvenida en esto: encuentro esto bastante intrigante.

Puntos a tener en cuenta:

  • No se bloquea en OS X.
  • No se bloquea construido O0.
¿Fue útil?

Solución

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

Debe copiar el bloque en la asignación o cuando se pasa a esta función. Si bien ARC resuelve el problema de auto-movido a retorno, no lo hace para los argumentos (no se puede hacer a las idiosincrasias de C).

Que no se estrella en ciertos entornos es simplemente una coincidencia; No se bloqueará mientras la versión de la pila del bloque no se haya sobrescribida. Una señal segura de esto es cuando tienes un bloqueo que desaparece con la optimización desactivada. Con la optimización apagada, el compilador no reutilizará la memoria de la pila dentro de un alcance dado, lo que hace que la memoria sea "válida" por mucho tiempo después de que sea.


Todavía no entiendo por qué no puede hacer un objc_blocketrain en lugar de un objc_etain normal. 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 mucho estado, incluidos, potencialmente, otros bloques, entonces el block_copy () podría ser realmente De Verdad De Verdad caro.

Es decir, si tuvieras algo como:

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

... y eso implicaba un block_copy () simplemente debido a la tarea, haría imposible usar bloques de manera consistente sin riesgo de problemas de rendimiento patológico. Porque no hay forma de que el compilador sepa si SomeRandomFunc() es sincrónico o asincrónico, no hay forma de administrar esto automáticamente (en este momento, estoy seguro de deshacerse de este posible triplewire).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top