ios - block in objective c
Bloques de iOS y referencias fuertes/débiles a uno mismo. (3)
Tengo una pregunta sobre las referencias fuertes y débiles a sí mismo en bloques en iOS. Sé que la forma correcta de referirse a uno mismo dentro de un bloque es crear una referencia débil fuera del bloque, y luego una referencia fuerte a esa referencia débil dentro del bloque, como esta:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
typeof(self) strongSelf = weakSelf;
NSLog(@"%@", strongSelf.someProperty);
});
Sin embargo, ¿qué pasa si tienes bloques anidados? ¿Es el único conjunto de referencias suficiente? ¿O necesitas un nuevo conjunto para cada bloque? Por ejemplo, ¿cuál de los siguientes es correcto?
Esta:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
typeof(self) strongSelf = weakSelf;
NSLog(@"%@", strongSelf.someProperty);
dispatch_async(dispatch_get_main_queue(), ^ {
strongSelf.view.frame = CGRectZero;
});
});
O esto:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
typeof(self) strongSelf = weakSelf;
NSLog(@"%@", strongSelf.someProperty);
__weak typeof(strongSelf) weakSelf1 = strongSelf;
dispatch_async(dispatch_get_main_queue(), ^ {
typeof(strongSelf) strongSelf1 = weakSelf1;
strongSelf1.view.frame = CGRectZero;
});
});
Cualquier información o explicación es muy apreciada!
Ambas construcciones están bien. Solo depende de tu intento. ¿Qué desea que suceda si el objeto (a) se libera después de que comience el bloque externo pero (b) antes de que comience el bloque interno en la cola principal? Si no quiere que se retenga en este escenario (lo que podría suponer que era su intención, dado que está realizando este ejercicio de weakSelf
en primer lugar), use su ejemplo final, donde tiene el segundo puntero débil. De lo contrario puedes usar tu otro ejemplo.
Dicho esto, un par de observaciones:
No es una conclusión
weakSelf
que tienes que usar este patrónweakSelf
en primer lugar. Algunas personas piensan erróneamente que tienen que usar este patrónweakSelf
para evitar un ciclo de referencia fuerte (también conocido como ciclo de retención). Pero este ejemplo de código no constituye un ciclo de referencia fuerte. Simplemente retiene el objeto mientras se ejecuta el código enviado, lo cual es una consideración muy diferente.De hecho, a veces necesitas / quieres eso. A veces no lo haces. Depende del problema de negocio que estés resolviendo. Absolutamente, con frecuencia no desea que se mantenga una fuerte referencia a
self
, en cuyo caso el patrónweakSelf
tiene perfecto sentido. Pero ese no es siempre el caso.Pero mi punto es que no deberías estar
weakSelf
este patrónweakSelf
(al menos en este escenariodispatch_async
) para evitar un ciclo de referencia fuerte. No existe tal ciclo. Cuando se trata de un problema, es cuando tiene una variable de bloque (por ejemplo, un bloque de FinalizaciónHandler). En ese caso, el patrónweakSelf
es crítico. Pero no aquí.Pero consideremos por un momento ese escenario en el que no quieres que te retengas a ti
self
. Luego hay una pregunta de si desea que el código enviado continúe en primer lugar. Si no, tal vez debería estar usando una cola de operaciones con operaciones cancelables en lugar de GCD.Por ejemplo, me sorprende la frecuencia con la que las personas se preocupan por conservar el controlador de vista mientras se ejecuta alguna solicitud de red en segundo plano, pero no se preocupe por si deberían cancelar esa solicitud de red de fondo en primer lugar. A menudo, esta última es una consideración de diseño mucho más significativa (por ejemplo, el PDF o la imagen que está descargando ocupa muchos más recursos del sistema (tanto la memoria como el ancho de banda de la red) que el controlador de vista nunca).
Pero supongamos que (a) realmente quieres que el código enviado continúe ejecutándose, pero (b) no quieres mantenerte. (Esto parece un escenario raro, pero es el que usted ha preguntado, así que prosigamos con eso). La pregunta final de si también necesita la construcción
strongSelf
. En su caso, cuando solo está llamando a un solo método deself
, no necesita molestarse con esta construcciónstrongSelf
. Eso es crítico solo si vas a deferirte a ivars o de lo contrario necesitas evitar las condiciones de la carrera. Pero, en este ejemplo, dado que un mensaje enviado a un objetonil
no hace nada, técnicamente a menudo no necesita preocuparse por esta construcciónstrongSelf
.
No me malinterpretes Es bueno weakSelf
los brazos alrededor del patrón weakSelf
, así como el patrón anidado strongSelf
que a veces lo acompaña. Solo estoy sugiriendo que es bueno entender cuándo se necesitan realmente estos patrones. Y creo que la elección de GCD frente a una NSOperation
cancelable es a menudo una pregunta mucho más crítica, pero que a menudo se pasa por alto.
Los bloques se crean y almacenan en la pila. Por lo tanto, el bloque se destruirá cuando vuelva el método que creó el bloque.
Si un bloque se convierte en una variable de instancia ARC, copie el bloque de la pila al montón. Puedes copiar explícitamente un bloque con el mensaje de copia. Su bloque ahora es un bloque basado en el montón en lugar de un bloque basado en la pila. Y tienes que lidiar con algunos problemas de gestión de memoria. El bloque en sí mantendrá una fuerte referencia a cualquier objeto al que haga referencia. Declare __ punteros débiles fuera del bloque y luego haga referencia a este puntero dentro del bloque para evitar la retención de ciclos.
No necesitas hacer dos conjuntos de referencias débiles. Lo que quiere evitar con los bloques es un ciclo de retención, dos objetos que se mantienen vivos innecesariamente.
Si tengo un objeto con esta propiedad:
@property (strong) void(^completionBlock)(void);
y tengo este método:
- (void)doSomething
{
self.completionBlock = ^{
[self cleanUp];
};
[self doLongRunningTask];
}
el bloque se mantendrá activo cuando lo guarde en la propiedad completionBlock
. Pero como se refiere a self
dentro del bloque, el bloque se mantendrá vivo hasta que desaparezca, pero esto no sucederá, ya que ambos se están refiriendo entre sí.
En este método:
- (void)doSomething
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self cleanUp];
}];
[self doLongRunningTask];
}
No necesitas hacer una referencia débil a ti self
. El bloque se mantendrá vivo, ya que hace referencia a self
desde dentro, pero como lo único que estamos haciendo es transferir el bloque a [NSOperationQueue mainQueue]
, el [NSOperationQueue mainQueue]
no mantiene el bloque vivo.
Espero que esto ayude.