objective c - recursividad - bloque recursivo y retener ciclos en ARC
que es ser recursivo (3)
La respuesta es no.
Parece que no podemos hacer nada mejor que usar el calificador __block.
__block void(^strawberryFields)();
strawberryFields = ^{ strawberryFields(); };
strawberryFields();
Gracias al artículo de Bill Bumgarner sobre bloques.
EDITAR:
__block __weak void(^strawberryFields)();
parece ser la forma preferida bajo ARC.
EDIT2:
No. La respuesta sugerida es acerca de las llamadas asincrónicas . Deseo y necesito llamadas sincrónicas, como en una llamada recursiva estándar normal.
EDITAR:
mientras
__unsafe_unretained void (^unsafe_apply)(UIView *, NSInteger) ;
compila sin advertencia o errores, falla en tiempo de ejecución con un NULL almacenado en inseguro_apply.
Sin embargo esto:
- (void) applyToView: (UIView *) view {
UIColor * (^colorForIndex)(NSInteger) = ^(NSInteger index) {
return [UIColor colorWithHue: ((CGFloat) index / 255.0f)
saturation: 0.5f
brightness: 0.5f
alpha: 1.0f] ;
} ;
void (^applyColors) (UIView *, NSInteger index) = ^(UIView * view, NSInteger index) {
view.backgroundColor = colorForIndex(index) ;
} ;
void (^__block recurse_apply)(UIView *, NSInteger) ;
void (^apply)(UIView *, NSInteger) = ^(UIView * view, NSInteger level) {
applyColors(view, level) ;
[view.subviews enumerateObjectsUsingBlock:^(UIView * subview, NSUInteger idx, BOOL *stop) {
recurse_apply(subview, 1+level) ;
}] ;
} ;
recurse_apply = apply ;
apply(view, 0) ;
}
compila sin advertencias, pero lo más importante, realmente se ejecuta.
¡Pero esto es tan feo !
considere (colorear la jerarquía de vista, para exponer el propósito ...) :
- (void) applyToView: (UIView *) view {
UIColor * (^colorForIndex)(NSInteger) = ^(NSInteger index) {
return [UIColor colorWithHue: ((CGFloat) (index * 10.0f) / 255.0f)
saturation: 0.5f
brightness: 0.5f
alpha: 1.0f] ;
} ;
void (^applyColors) (UIView *, NSInteger index) = ^(UIView * view, NSInteger index) {
view.backgroundColor = colorForIndex(index) ;
} ;
void (^apply)(UIView *, NSInteger) = ^(UIView * view, NSInteger level) {
applyColors(view, level) ;
[view.subviews enumerateObjectsUsingBlock:^(UIView * subview, NSUInteger idx, BOOL *stop) {
apply(subview, 1+level) ;
}] ;
} ;
apply(view, 0) ;
}
Recibo esta advertencia:
/Users/verec/Projects/solotouch/SoloTouch/BubbleMenu.m:551:42: la Block pointer variable ''apply'' is uninitialized when captured by block
Si aplico la solución sugerida: Maybe you meant to use __block ''apply''
void (^__block apply)(UIView *, NSInteger) = ^(UIView * view, NSInteger level) {
Luego obtengo: /Users/verec/Projects/solotouch/SoloTouch/BubbleMenu.m:554:13: Capturing ''apply'' strongly in this block is likely to lead to a retain cycle
Intenté varias formas de alterar el código y deshacerme de esas advertencias
__weak typeof (apply) wapply = apply ;
if (wapply) {
__strong typeof (wapply) sappy = wapply ;
wapply(subview, 1+level) ;
}
Pero las cosas empeoran y se convierten en errores.
Terminé con esto:
__unsafe_unretained void (^unsafe_apply)(UIView *, NSInteger) ;
void (^apply)(UIView *, NSInteger) = ^(UIView * view, NSInteger level) {
applyColors(view, level) ;
[view.subviews enumerateObjectsUsingBlock:^(UIView * subview, NSUInteger idx, BOOL *stop) {
unsafe_apply(subview, 1+level) ;
}] ;
} ;
unsafe_apply = apply ;
apply(view, 0) ;
¿Alguien tiene una mejor solución, donde podría hacer todo desde dentro del bloque y no espantosamente hacer el parche como tenía que hacer aquí?
Tenga en cuenta que esas preguntas SO son sobre la captura de self
y esas preguntas tan no tienen ninguna respuesta satisfactoria.
__block
capturar una variable __block
, porque los bloques capturan variables no __block
por valor cuando se crean, y la asignación ocurre después de que se ha creado el bloque.
En ARC, __block
variables del tipo puntero de objeto (generalmente todas las variables son implícitamente __strong
) son retenidas por el bloque. Entonces, si el bloque captura una variable __block
que apunta a sí misma, crearía un ciclo de retención. La solución es hacer que capture una referencia débil. En las versiones del sistema operativo que admiten __weak
, __weak
debe usarse en lugar de __unsafe_unretained
.
Sin embargo, si la única referencia al bloque fuera una variable __weak
, no habría referencias fuertes al bloque, lo que significa que puede desasignarse. Para usar el bloque, debe tener una referencia fuerte para mantenerlo.
Por lo tanto, necesitas dos variables, una débil y una fuerte. La forma correcta de hacerlo en ARC es:
__block __weak void (^weak_apply)(UIView *, NSInteger) ;
void (^apply)(UIView *, NSInteger) ;
weak_apply = apply = ^(UIView * view, NSInteger level) {
applyColors(view, level) ;
[view.subviews enumerateObjectsUsingBlock:^(UIView * subview, NSUInteger idx, BOOL *stop) {
weak_apply(subview, 1+level) ;
}] ;
} ;
apply(view, 0) ;
Para evitar las advertencias de ARC y desarrollar la respuesta de @newacct, encontré que funciona el bloqueo débil dentro del bloque retenido:
//a block definition
typedef void (^CompletionType)(NSDictionary * __nullable response, NSError * __nullable error);
//a block calling itself without the ARC retain warning
__block CompletionType completionBlock = nil;
__block __weak CompletionType weakCompletionBlock = nil;
completionBlock = ^(NSDictionary *response, NSError *error) {
weakCompletionBlock = completionBlock;
weakCompletionBlock();
});
};