Uso adecuado de iOS de @weakify(self) y @strongify(self)
objective-c libextobjc (3)
Estoy empezando a integrar libextobjc ( https://github.com/jspahrsummers/libextobjc ) en mi aplicación iOS principalmente para aprovechar @strongify
y @weakify
, pero tengo algunas preguntas antes de proceder demasiado profundo en el proceso.
Aquí hay un ejemplo intencionalmente excesivamente complicado para tratar de descubrir cómo manejar esto:
- (void)someMethod {
if (self.someBOOL) {
_someObjectInstanceVar = [Object objectWithCompletionHandler:^{
// self reference #1
if (self.someProperty) {
// self reference #2
[[Async HTTPRequest] sendAWithID:self.property.id completionHandler:^(void (^)(NewObject *newObject) {
// self reference #3
[self showViewWithObject:newObject handler:^{
// self reference #4
[self reloadData];
}];
}];
}
}];
else {
[[Async HTTPRequest] sendBWithID:self.property.id completionHandler:^{
// self reference #5
[self reloadData];
}];
}
}
Tengo entendido que si quiero hacer algo como una solicitud HTTP asincrónica, y dentro del autor de referencia del manejador de finalización, como [self reloadData]
, no necesito hacer nada con fuerte / débil ya que el bloque de solicitud en sí no es reteniendo el bloque de finalización, por lo que no hay problemas con retener los ciclos allí. En el ejemplo de código anterior, creo que # 5 es un caso en el que retener ciclos no es un problema.
La principal preocupación son todos los objetos que toman un bloque como propiedad / init param, que se aferran internamente a las propiedades del bloque. Dentro del método objectWithCompletionHandler
, donde someObject
mantiene en el bloque completionHandler como una variable de instancia, hay múltiples referencias a self que sé que causarían una fuga. Mi principal pregunta es en tal caso, ¿cómo necesitarías manejar el weakify
y el strongify
para hacerlo "más seguro"? ¿Sería suficiente una llamada @weakify y @strongify cada una, como la siguiente:
- (void)someMethod {
@weakify (self);
_someObjectInstanceVar = [Object objectWithCompletionHandler:^{
@strongify(self);
...
}
¿La referencia de @strongify(self)
sería suficiente para utilizar para las auto referencias # 1, 2, 3 y 4, o tengo que hacerlo (e incluso podría funcionar) obtener una nueva referencia débil / fuerte para usar dentro de sendAWithID
método y el reloadData
anidado?
EDITAR: corrigió el código para que la pregunta tenga más sentido y corrige algunos errores de sintaxis.
Cómo funciona @strongify
Después de @strongify
, self
tendrá una dirección de puntero diferente dentro del bloque de lo que será fuera del bloque. Eso es porque @strongify
declara una nueva variable local llamada self
cada vez. (Esta es la razón por la que suprime la advertencia -Wshadow
, que "advierte siempre que una variable local sombrea otra variable local"). Vale la pena leer y comprender la implementación de estas funciones . Entonces, aunque los nombres son los mismos, trátelos como referencias strong
separadas.
Usando @strongify
en tu código
Suponiendo (lo cual no es verdad) que cada uso de un bloque crearía un ciclo de referencia , usted podría:
- (void)someMethod {
if (self.someBOOL) {
@weakify(self);
_someObjectInstanceVar = [Object objectWithCompletionHandler:^{
@strongify(self);
// self reference #1
if (self.someProperty) {
@weakify(self);
// self reference #2
[[Async HTTPRequest] sendAWithID:self.property.id completionHandler:^(void (^)(NewObject *newObject) {
@strongify(self);
// self reference #3
@weakify(self);
[self showViewWithObject:newObject handler:^{
// self reference #4
@strongify(self);
[self reloadData];
}];
}];
}
}];
// etc…
}
Sin embargo, recuerde que después del primer uso de @strongify
, self
se referirá a variables de pila locales . Por lo general, se destruirán cuando finalice el alcance en el que están definidos (siempre que no los almacene en propiedades ni los use en un bloque anidado). Por lo tanto, en función del código que mostró, solo lo necesita después de // self reference #1
.
Ver también
Leer la prueba unitaria que cubre @weakify
y @strongify
ayudará a aclarar el uso correcto de estas funciones.
Llamar a "self" dentro del bloque que en hold by "self" conducirá a "Retain Cycles" y por lo tanto a pérdida de memoria. Así que, idealmente, dice así:
@interface A: NSObject // Some interface A
@property (nonatomic, copy, readwrite) dispatch_block_t someBlock; // See block is strongly retained here.
@end
*******************************************************
@implementation A
- (void) someAPI
{
__weak A * weakSelf = self; // Assign self to weakSelf and use it
// enter code here inside block to break retain cycles.
self.someBlock =
^{
A * strongSelf = weakSelf; // Assign weak self to strongSelf before
// using it. This is because weakSelf can go nil anytime and it may happen
// that only few lines from block get executed before weakSelf goes nil,
// and hence code may be in some bad state.
if (strongSelf != nil)
{
// Use strongSelf.
[strongSelf doSomethingAwesome];
[strongSelf doSomethingAwesomeAgain];
}
};
}
@end
Si el bloque no es retenido por "self", entonces es seguro usar solo "self" dentro de los bloques y no crearán ciclos de retención.
Nota: El concepto de administración de memoria sigue siendo el mismo con el uso de la biblioteca "libextobjc".
Para responder a su pregunta sobre si funcionan varias instancias de debilitar / fortalecer en cada nivel anidado de sus bloques, entonces sí. Pero no hay necesidad de hacerlo porque su primera definición de @strongify ya define auto para todo el alcance interno de su bloque (y los bloques que están anidados en él).
Sin embargo, dado que sus bloques tienen diferentes tiempos de vida, es posible que desee agregar @strongify para cada bloque anidado para asegurarse de que todos tengan su propio ciclo de retención en su alcance interno.
Aquí está el hilo de problema de github que explica este caso: https://github.com/jspahrsummers/libextobjc/issues/45