variable unowned strong collector arc ios objective-c uiviewcontroller objective-c-blocks retain-cycle

ios - unowned - ¿Es realmente necesario el baile weakelf/strongSelf cuando se hace referencia a uno mismo dentro de una finalización no retenida llamada desde un UIViewController?



unowned swift 4 (4)

Como creo que ha sido diagnosticado correctamente, usar self mismo no necesariamente generará un ciclo de referencia fuerte en este escenario. Pero esto retendrá el controlador de vista mientras se completa la operación de red, y en este caso (como en la mayoría de los casos), no es necesario. Por lo tanto, puede que no sea necesario utilizar weakSelf , pero probablemente sea prudente hacerlo. Minimiza la posibilidad de un ciclo de referencia fortuito accidental y permite un uso más eficiente de la memoria (liberando la memoria asociada al controlador de vista tan pronto como se descarta ese controlador de vista en lugar de retener innecesariamente el controlador de vista hasta que se completa la operación de red) .

Sin embargo, no es necesario construir strongSelf . Usted puede:

- (void)makeAsyncNetworkCall { __weak typeof(self) weakSelf = self; [self.networkService performAsyncNetworkCallWithCompletion:^{ dispatch_async(dispatch_get_main_queue(), ^{ [weakSelf.activityIndicatorView stopAnimating]; }); }]; }

Solo necesita la combinación weakSelf / strongSelf donde es fundamental tener una referencia fuerte (por ej., Está desreferenciando ivars) o si necesita preocuparse por las condiciones de carrera. Ese no parece ser el caso aquí.

Supongamos que tengo el siguiente método dentro de una subclase UIViewController :

- (void)makeAsyncNetworkCall { [self.networkService performAsyncNetworkCallWithCompletion:^{ dispatch_async(dispatch_get_main_queue(), ^{ [self.activityIndicatorView stopAnimating]; } }); }]; }

Sé que la referencia a self dentro del bloque da como resultado que el bloque UIViewController instancia de UIViewController . Siempre que performAsyncNetworkCallWithCompletion no almacene el bloque en una propiedad (o ivar) en mi NetworkService , ¿estoy en lo cierto al pensar que no hay un ciclo de retención?

Me doy cuenta de que esta estructura anterior conducirá a que el UIViewController se conserve hasta que finalice la ejecución de la llamada de sincronización de performAsyncNetworkCallWithCompletion , incluso si el sistema la ha liberado antes. Pero es probable (¿o posible?) UIViewController el sistema desasigne mi UIViewController ( después de los cambios en la forma en que iOS 6 administra una memoria CALayer respaldo de CALayer )?

Si hay una razón por la que debo hacer la danza "weakSelf / strongSelf", se vería así:

- (void)makeAsyncNetworkCall { __weak typeof(self) weakSelf = self; [self.networkService performAsyncNetworkCallWithCompletion:^{ typeof(weakSelf) strongSelf = weakSelf; if (!strongSelf) { return; } dispatch_async(dispatch_get_main_queue(), ^{ [strongSelf.activityIndicatorView stopAnimating]; } }); }]; }

Pero esto me parece desmesuradamente feo y me gustaría evitarlo si no es necesario.


Creo que el problema es que networkService puede mantener una fuerte referencia al bloque. Y el controlador de vista puede tener una fuerte referencia al servicio de red. Por lo tanto, podría existir el posible ciclo de VC-> NetworkService-> block-> VC. Sin embargo, en este caso, generalmente es seguro suponer que el bloque se lanzará después de que se haya ejecutado, en cuyo caso el ciclo se interrumpirá. Entonces, en este caso, no es necesario.

Donde es necesario es si el bloque no se libera. Digamos, en lugar de tener un bloque que se ejecuta una vez después de una llamada de red, tiene un bloque que se utiliza como devolución de llamada. es decir, el objeto networkService mantiene una fuerte referencia al bloque y lo usa para todas las devoluciones de llamada. En este caso, el bloque tendrá una fuerte referencia al VC, y esto creará un ciclo fuerte, por lo que se prefiere una referencia débil.


La respuesta no es tan simple aquí. Estoy de acuerdo con la respuesta de @ Rob, pero necesita una explicación adicional:

  1. __weak se considera una forma segura, ya que anula el self cuando se libera, lo que significa que no habrá excepción si la devolución de llamada ocurre mucho más tarde cuando el objeto que llama ya está liberado, referenciado por bloque, como UIViewController extraído de la pila. Agregar la posibilidad de cancelar cualquier tipo de operación es solo una cuestión de higiene y quizás también de recursos. Puede, por ejemplo, simplemente cancelar NSURLConnection , no solo se puede cancelar la NSOperation , puede cancelar lo que se está ejecutando de forma asíncrona en el método que devuelve la llamada al bloqueo.

  2. Si el self permite que el bloque conserve, la historia puede complicarse un poco si el objeto llamador como UIViewController es liberado por UINavigationController y el bloque aún lo retiene y devuelve la llamada. En este caso, se ejecutará el bloque de devolución de llamada y se asumirá que algunos datos se modificarán por los resultados de este. Eso podría ser un comportamiento deseado, pero en la mayoría de los casos no. Por lo tanto, la cancelación de la operación puede ser más importante en este caso, por lo que es muy inteligente en los métodos UINavigationControllerDelegate al cancelar las tareas asíncronas de la colección mutable que reside en UINavigationController como objeto asociado o como singleton.

Guardar apuesta es con la primera opción, por supuesto, pero solo en el caso de que no desee que continúe la operación asíncrona después de cerrar el objeto que llama.


No, si su self.networkService no lo usa como propiedad de bloque, debería estar bien