ios objective-c libextobjc

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