¿Siempre pasas la referencia débil de uno mismo al bloque en ARC?

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í en lugar de su referencia débil. ¿Puede ser esa la causa de que estos bloques se retengan y eviten que se los desmantele? La pregunta es, ¿debería usar siempre una referencia weakself 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); } } 

Ayuda a no enfocarse en la parte strong o weak de la discusión. En lugar de enfocarse en la parte del ciclo .

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

  • El Objeto A no será desasignado porque el Objeto B contiene una referencia al mismo.
  • Pero el Objeto B no será desasignado siempre que el Objeto A tenga una referencia al mismo.
  • Pero el Objeto A nunca será desasignado porque el Objeto B contiene una referencia al mismo.
  • indefinidamente

Por lo tanto, esos dos objetos se quedarán en la memoria durante la vida útil del progtwig aunque deberían desasignarse, si todo funcionara correctamente.

Entonces, lo que nos preocupa es retener los ciclos , y no hay nada sobre bloques en sí mismos que creen estos ciclos. Esto no es un problema, por ejemplo:

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

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

Donde te metes en problemas es algo así 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 referencia strong explícita al bloque. Y el bloque tiene una fuerte referencia implícita al self . Eso es un ciclo, y ahora ninguno de los objetos será desasignado correctamente.

Debido a que, en una situación como esta, el 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 el bloque lo use:

 __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 se sigue cuando se trata de bloques que se self ! Esto solo se debe usar para romper lo que de otra manera 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 se desasignó el self .

 //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]; }]; 

No tiene que usar siempre una referencia débil. Si su bloque no se retiene, pero se ejecuta y luego se descarta, puede capturar uno mismo con fuerza, ya que no creará un ciclo de retención. En algunos casos, incluso desea que el bloque se retenga hasta la finalización del locking 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.

Estoy totalmente de acuerdo con @jemmons.

“Pero este no debería ser el patrón predeterminado que se sigue cuando se trata de bloques que se llaman a sí mismos. Esto solo se debe usar para romper lo que de otra manera sería un ciclo de retención entre uno mismo y el bloque. corrí el riesgo de pasar un bloque a algo que se ejecutó después de que se desasignó el auto “.

 //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 uno puede definir una referencia fuerte sobre el weakSelf dentro del bloque.

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

Como Leo señala, el código que agregaste a tu pregunta no sugiere un ciclo de referencia fuerte (también conocido como, mantener el ciclo). Un problema relacionado con la operación que podría causar un fuerte ciclo de referencia sería si la operación no se está liberando. Aunque el fragmento de código sugiere que no ha definido que su operación sea simultánea, pero si la tiene, no se lanzará si nunca ha publicado isFinished , o si tiene dependencias circulares, o algo así. Y si la operación no se libera, tampoco se lanzará el controlador de vista. Sugeriría agregar un punto de interrupción o NSLog en el método dealloc su operación y confirmar que se está llamando.

Tu dijiste:

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

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

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

Si todavía no se libera el controlador de vista, simplemente debe identificar dónde reside la referencia fuerte no resuelta (suponiendo que confirmó que la NSOperation se desasigna). Un ejemplo común es el uso de un NSTimer repetitivo. O algún delegate personalizado u otro objeto que mantiene erróneamente una referencia strong . A menudo puede usar los instrumentos para rastrear dónde los objetos obtienen sus fuertes referencias, por ejemplo:

registrar recuentos de referencia en Xcode 6

O en Xcode 5:

registrar recuentos de referencia en Xcode 5

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 sólidas, se mantienen vivos unos a otros, incluso si no hay referencias fuertes desde fuera del grupo.] Para obtener más información, lea el documento

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"; };