2012-06-24 11 views
8

Estoy trabajando en una tarea (iOS5 + solamente) que implica la descarga de miles de imágenes desde el servidor. Las imágenes pertenecen a ciertas categorías y cada categoría puede tener cientos de imágenes. Lo que necesito hacer es esto: -AFNetworking + NsOperationQueue - Descargar miles de imágenes

1) Asegúrese de que la aplicación descarga las imágenes que faltan en el fondo si la aplicación está activa (incluso cuando el usuario está navegando en otras áreas de la aplicación que no están relacionadas con las fotos) .

2) Cuando el usuario hace clic en una categoría de fotografía, las imágenes en esa categoría se deben descargar como alta prioridad porque esas son las que deben ser visibles inmediatamente.

Todo lo anterior ocurre solo si la imagen no está disponible sin conexión. Una vez que se haya descargado, la imagen se usará desde el almacenamiento local.

Para solucionar esto, la lógica que uso es la siguiente: -

1) En AppDelegate.m, en applicationDidBecomeActive, comienzo downlading alguna de las imágenes que faltan. Para hacer esto, realizo una consulta de Datos centrales, descubro qué imágenes faltan y comienzo a descargarlas en un subproceso con prioridad de FONDO. Algo como esto: -

dispatch_queue_t imageDownloadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); 
dispatch_async(imageDownloadQueue, ^{ 
    [DataDownloader downloadMissingImages]; 
}); 
dispatch_release(imageDownloadQueue); 

El código downloadMissingImages se ve así: -

NSOperationQueue *downloadQueue = [[NSOperationQueue alloc] init]; 
     downloadQueue.maxConcurrentOperationCount = 20; 

     for(MyImage *img in matches) 
     { 
      NSURLRequest *request = [NSURLRequest requestWithURL:img.photoUrl]; 
      AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request success:^(UIImage *image) { 

       [MyImage imageFromAPI:image inManagedObjectContext:document.managedObjectContext]; 

       NSLog(@"Successfully downloaded image for %@", img.title);  
      }]; 

      [downloadQueue addOperation:operation]; 
     } 

Esto funciona, pero bloquea la interfaz de usuario principal y la aplicación se bloquea después de un tiempo. Esto es cuando intento descargar aproximadamente 700 imágenes. Con más imágenes, ciertamente se bloqueará.

2) Cuando un usuario hace clic en una categoría, necesito descargar esas imágenes primero, ya que deben mostrarse al usuario de inmediato. No estoy seguro de cómo puedo interrumpir la llamada a las imágenes que faltan y decirle que comience a descargar ciertas imágenes antes que los demás.

Así que, básicamente, necesito descargar todas las imágenes que faltan en el fondo, pero si el usuario está navegando categoría de la foto, esas imágenes deben tener alta prioridad en la cola de descarga.

No entiendo cómo hacer que esto funcione de manera eficiente. ¿Alguna idea?

registros

El choque se ven así

PAPP(36373,0xb065f000) malloc: *** mmap(size=16777216) failed (error code=12) 
*** error: can't allocate region 
*** set a breakpoint in malloc_error_break to debug 
PAPP(36373,0xb065f000) malloc: *** mmap(size=16777216) failed (error code=12) 
*** error: can't allocate region 
*** set a breakpoint in malloc_error_break to debug 
Jun 24 11:39:45 MacBook-Pro.local PAPP[36373] <Error>: ImageIO: JPEG Insufficient memory (case 4) 

Gracias de antemano.

Respuesta

3

sobre el accidente, supongo que su aplicación se mató debido a cualquiera de las dos opciones:

  1. la aplicación deje de responder (y por lo tanto no responde al proceso centinela IOS);

  2. demasiada memoria utilizada en el ciclo creando más de 700 operaciones de solicitud.

Para aclarar lo que realmente está sucediendo, debe proporcionar más información sobre el bloqueo (el registro de la consola). En cualquier caso, la solución sería cargar las imágenes en trozos de quizás 10 o 20 cada uno (podría ir incluso 1 por 1, si lo desea, no veo mucho problema con eso).

Sobre el segundo punto, ¿qué pasa con esto:

  1. descarga una imagen de mayor prioridad en el hilo principal (a través de un enlace asíncrono, por supuesto, para evitar el bloqueo de la interfaz de usuario);

  2. antes de iniciar la descarga de una imagen "fuera de línea", compruebe si la imagen ya se ha descargado mientras tanto a través de una descarga de "prioridad más alta".

Para manejar bien el punto 2, probablemente tendría que hacer cola su operación personalizado en lugar de un AFImageRequestOperation con el fin de hacer el registro de entrada antes de la descarga real.

EDIT:

Sobre la descarga de imágenes en trozo, lo que podría hacer es usar los grupos de despacho para agrupar sus operaciones de redes:

dispatch_group_t group = dispatch_group_create(); 

<your_core_data_query> 

for (...) { 
    dispatch_group_enter(group); 

     NSURLRequest *request = [NSURLRequest requestWithURL:img.photoUrl]; 
     AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request success:^(UIImage *image) { 
      [MyImage imageFromAPI:image inManagedObjectContext:document.managedObjectContext]; 
      NSLog(@"Successfully downloaded image for %@", img.title); 

      dispatch_group_leave(group);  //<== NOTICE THIS 
     }]; 

} 

dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 
dispatch_release(group); 

En esta muestra, que estoy usando un grupo de despacho al grupo A número de operaciones asíncronas y espere a que se ejecuten todas; cuando devuelve dispatch_group_wait, puede ejecutar otra ronda de eso (consultar datos básicos y luego enviar operaciones).

Acerca de su otra pregunta (cómo verifico si una cola de mayor prioridad ya descargó una imagen determinada), debe hacer una consulta de datos centrales antes de ejecutar cada AFImageRequestOperation; una posibilidad es derivar su propia clase y anular el método start para realizar el control.

En ambas cuentas, podría simplificar mucho la lógica de todo esto descargando las imágenes una por vez (es decir, el bucle for (...) no estaría allí; simplemente consulte la siguiente imagen para descargar y descargar; antes de descargar comprobar que no está ya allí.

sugeriría ir este camino más fácil.

espero que ayude.

+0

Hola Sergio, ¿tienes cualquier código de ejemplo para demostrar descargas fragmentada como usted sugiere? –

+0

también , ¿cómo puedo verificar si una cola de mayor prioridad ya descargó una ce ¿alguna imagen? ¿Algún código de muestras para eso? –

+0

He modificado la pregunta para incluir registros de fallos: genero miniaturas de cada descarga de imagen ... así que creo que 700 imágenes definitivamente están causando la pérdida de memoria aquí ... –

Cuestiones relacionadas