Bloqueo de finalización de networking, recursion y ciclo de retención de ARC

Esta es una pregunta complicada, a la que la respuesta podría ser útil para muchos aprendices de networkinges, incluido yo.

Alguna información de background sobre el context:

  • Digamos que desea download datos de un Servicio en línea
  • quieres hacer esto de forma asincrónica
  • desea hacer una descarga a la vez, luego tal vez hacer otra al completar la anterior.

Una forma clara de hacerlo es usar la recursion. El problema con las implementaciones comunes que puedes encontrar es Retain cycles, entre bloques de finalización de networking y self. Esto se puede resolver utilizando los indicadores de reference weakSelf.

Pero, ¿qué hay de retener ciclos para llamadas recursivas?

Hemos implementado una stack recursiva, auto apuntando a una class de gestión de descargas, como esta:

-(void)startNetworkDownloadForObjectAtIndex:(int) anIndex { __typeof__(self) __weak weakSelf = self; NSURL *urlForObjectAtIndex = [SomeClass URLforIndex:anIndex]; [self.downloadManager getResourceAtURL:urlForObjectAtIndex success:^(AFHTTPRequestOperation *operation, id responseObject) { if (indexOfObjectToDownload < weakself.totalNumberOfObjectsToDownload) [weakSelf startNetworkDownloadForObjectAtIndex:indexOfObjectToDownload+1]; else [weakSelf startDOwnloadTimer]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { // response code is in operation.response.statusCode [weakSelf handleNetworkError:error]; }]; } -(void)handleNetworkError:error { // Do some error handling [self startNetworkDownloadForObjectAtIndex:self.lastUnsentObjectIndex]; } -(void)startDownloadTimer { if (self.syncEngineTimer) [self.syncEngineTimer invalidate]; self.syncEngineTimer = [NSTimer scheduledTimerWithTimeInterval:kSyncTimeInterval target:self selector:@selector(restartNetworkDownload) userInfo:nil repeats:NO]; } -(void)restartNetworkDownload { // do some fancy calculations / etc to manage your download int anIndex = theResultOfYourCalculation; [self startNetworkDownloadForObjectAtIndex:anIndex]; } 

Bien, este es un ejemplo de una posible llamada recursiva de varias descargas de networking (obtenga 100 imágenes de parpadeo por ejemplo) e intente get las nuevas después de 1 hora, por ejemplo. Disculpe cualquier error de encoding.

Estamos ejecutando esto en ARC para dispositivos iOS por encima de iOS 5.0

Obviamente, rompimos el primer nivel de ciclo de retención mediante el uso de un indicador de reference weakSelf cuando usamos self.downloadManager manteniendo una reference al éxito y el bloque de finalización de falla. Todo esto está bien y va bien en los instrumentos.

Ahora, cuando miramos la asignación en Instrumentos, lanzamos una operación de descarga para varias descargas. Los instrumentos no muestran fugas. PERO cuando regurávelmente guarda el montón, puedes verlo crecer lentamente.

Al verificar las asignaciones y echar un vistazo a la stack de llamadas, definitivamente parece que el bloque está manteniendo una reference a uno mismo a través del uso de startDownloadTimer

Cualquier explicación sobre una posible causa y solución será muy apreciada 🙂

Tu timer conserva su objective (el self ).

Pruebe la solución a partir de esta pregunta: Referencia débil al objective NSTimer para evitar el ciclo de retención

O use dispatch_after lugar de timer.