ios iphone objective-c automatic-ref-counting weak-references

ios - ¿Pasar siempre la referencia débil del yo al bloque en ARC?



iphone objective-c (6)

Estoy un poco confundido sobre el uso de bloques en Objective-C. Actualmente uso ARC y tengo bastantes bloques en mi aplicación, actualmente siempre me refiero a mí self lugar de a su débil referencia. ¿Puede ser esa la causa de que estos bloques retengan el self y eviten que se desasigne? La pregunta es, ¿debería usar siempre una referencia weak de self en un bloque?

-(void)handleNewerData:(NSArray *)arr { ProcessOperation *operation = [[ProcessOperation alloc] initWithDataToProcess:arr completion:^(NSMutableArray *rows) { dispatch_async(dispatch_get_main_queue(), ^{ [self updateFeed:arr rows:rows]; }); }]; [dataProcessQueue addOperation:operation]; }

ProcessOperation.h

@interface ProcessOperation : NSOperation { NSMutableArray *dataArr; NSMutableArray *rowHeightsArr; void (^callback)(NSMutableArray *rows); }

ProcessOperation.m

-(id)initWithDataToProcess:(NSArray *)data completion:(void (^)(NSMutableArray *rows))cb{ if(self =[super init]){ dataArr = [NSMutableArray arrayWithArray:data]; rowHeightsArr = [NSMutableArray new]; callback = cb; } return self; } - (void)main { @autoreleasepool { ... callback(rowHeightsArr); } }


Algunas explicaciones ignoran una condición sobre el ciclo de retención [Si un grupo de objetos está conectado por un círculo de relaciones fuertes, se mantienen vivos entre sí, incluso si no hay referencias fuertes fuera del grupo.] Para obtener más información, lea el document


Así es como puedes usar el yo dentro del bloque:

// llamada del bloque

NSString *returnedText= checkIfOutsideMethodIsCalled(self);

NSString* (^checkIfOutsideMethodIsCalled)(*)=^NSString*(id obj) { [obj MethodNameYouWantToCall]; // this is how it will call the object return @"Called"; };


Ayuda no centrarse en la parte strong o weak de la discusión. En su lugar, centrarse en la parte del ciclo .

Un ciclo de retención es un ciclo que ocurre cuando el Objeto A retiene el Objeto B, y el Objeto B retiene el Objeto A. En esa situación, si se libera cualquiera de los objetos:

  • El objeto A no se desasignará porque el objeto B contiene una referencia a él.
  • Pero el Objeto B nunca será desasignado mientras el Objeto A tenga una referencia a él.
  • Pero el objeto A nunca será desasignado porque el objeto B contiene una referencia a él.
  • indefinidamente

Por lo tanto, esos dos objetos quedarán en la memoria durante la vida del programa, aunque deberían, si todo funcionara correctamente, ser desasignados.

Entonces, lo que nos preocupa es la retención de ciclos , y no hay nada acerca de los bloques en sí mismos que crean estos ciclos. Esto no es un problema, por ejemplo:

[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){ [self doSomethingWithObject:obj]; }];

El bloque retiene el self , pero el self no retiene el bloque. Si se libera uno u otro, no se crea ningún ciclo y todo se desasigna como debería.

Donde te metes en problemas es algo como:

//In the interface: @property (strong) void(^myBlock)(id obj, NSUInteger idx, BOOL *stop); //In the implementation: [self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) { [self doSomethingWithObj:obj]; }];

Ahora, su objeto ( self ) tiene una strong referencia explícita al bloque. Y el bloque tiene una fuerte referencia implícita a self . Eso es un ciclo, y ahora ninguno de los objetos se desasignará correctamente.

Debido a que, en una situación como esta, self por definición ya tiene una strong referencia al bloque, generalmente es más fácil de resolver al hacer una referencia explícitamente débil a self para que lo use el bloque:

__weak MyObject *weakSelf = self; [self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) { [weakSelf doSomethingWithObj:obj]; }];

¡Pero este no debería ser el patrón predeterminado que sigues al tratar con bloques que se llaman a self ! Esto solo debe usarse para romper lo que de otro modo sería un ciclo de retención entre uno mismo y el bloque. Si adoptara este patrón en todas partes, correría el riesgo de pasar un bloque a algo que se ejecutó después de que el self fue desasignado.

//SUSPICIOUS EXAMPLE: __weak MyObject *weakSelf = self; [[SomeOtherObject alloc] initWithCompletion:^{ //By the time this gets called, "weakSelf" might be nil because it''s not retained! [weakSelf doSomething]; }];


Como señala Leo, el código que agregó a su pregunta no sugeriría un ciclo de referencia fuerte (también conocido como ciclo de retención). Un problema relacionado con la operación que podría causar un fuerte ciclo de referencia sería si la operación no se libera. Si bien el fragmento de código sugiere que no ha definido que su operación sea concurrente, pero si lo hizo, no se liberaría si nunca se publicara el isFinished , o si tuviera dependencias circulares, o algo así. Y si la operación no se libera, el controlador de vista tampoco se liberaría. Yo sugeriría agregar un punto de interrupción o NSLog en el método de dealloc su operación y confirmar que se está llamando.

Tu dijiste:

Entiendo la noción de los ciclos de retención, pero no estoy muy seguro de lo que sucede en bloques, por lo que me confunde un poco.

Los problemas del ciclo de retención (ciclo de referencia fuerte) que ocurren con los bloques son como los problemas del ciclo de retención con los que está familiarizado. Un bloque mantendrá referencias sólidas a cualquier objeto que aparezca dentro del bloque y no liberará esas referencias fuertes hasta que se libere el propio bloque. Por lo tanto, si el bloque hace referencia a self , o incluso simplemente hace referencia a una variable de instancia de self , que mantendrá una fuerte referencia a self, no se resolverá hasta que se libere el bloque (o en este caso, hasta que se NSOperation subclase NSOperation .

Para obtener más información, consulte la sección Evitar ciclos de referencia fuertes al capturar uno mismo del documento Programación con Objective-C: Trabajar con bloques .

Si su controlador de vista aún no se está liberando, simplemente debe identificar dónde reside la referencia sólida no resuelta (suponiendo que confirmó que la NSOperation se está desasignando). Un ejemplo común es el uso de un NSTimer repite. O algún delegate personalizado u otro objeto que está manteniendo erróneamente una referencia strong . A menudo, puede usar Instrumentos para rastrear dónde los objetos obtienen sus referencias sólidas, por ejemplo:

O en Xcode 5:


Estoy totalmente de acuerdo con @jemmons.

"¡Pero este no debería ser el patrón por defecto que sigues cuando tratas con bloques que se llaman a sí mismo! Esto solo debe usarse para romper lo que de otra manera sería un ciclo de retención entre el yo y el bloque. Si fueras a adoptar este patrón en todas partes, deberías '' "Corro el riesgo de pasar un bloque a algo que se ejecutó después de que se autoasignara".

//SUSPICIOUS EXAMPLE: __weak MyObject *weakSelf = self; [[SomeOtherObject alloc] initWithCompletion:^{ //By the time this gets called, "weakSelf" might be nil because it''s not retained! [weakSelf doSomething]; }];

Para superar este problema, se puede definir una referencia fuerte sobre el debself dentro del bloque.

__weak MyObject *weakSelf = self; [[SomeOtherObject alloc] initWithCompletion:^{ MyObject *strongSelf = weakSelf; [strongSelf doSomething]; }];


No tienes que usar siempre una referencia débil. Si su bloque no se conserva, pero se ejecuta y luego se descarta, puede capturarse con fuerza, ya que no creará un ciclo de retención. En algunos casos, incluso desea que el bloque retenga el self hasta que se complete el bloque para que no se desasigne prematuramente. Sin embargo, si captura el bloque con fuerza y ​​dentro de la captura, creará un ciclo de retención.