2012-06-24 3 views
8

Sto lavorando a un'attività (solo iOS5 +) che comporta il download di migliaia di immagini dal server. Le immagini appartengono a determinate categorie e ogni categoria può avere centinaia di immagini. Quello che devo fare è questo: -AFNetworking + NsOperationQueue - Download di migliaia di immagini

1) Assicurarsi che l'app scarichi eventuali immagini mancanti in background se l'app è attiva (anche quando l'utente sta navigando in altre aree dell'app che non sono correlate alle foto) .

2) Quando l'utente fa clic su una categoria di foto, le immagini di quella categoria devono essere scaricate come priorità elevata poiché sono quelle che devono essere immediatamente visibili.

Tutto ciò succede solo se l'immagine non è già disponibile offline. Una volta scaricato, l'immagine verrà utilizzata dalla memoria locale.

Per risolvere questo problema, la logica che sto usando è questo: -

1) In AppDelegate.m, in applicationDidBecomeActive, comincio downlading eventuali immagini mancanti. Per fare questo, faccio una query di Core Data, scopro quali sono le immagini mancanti e inizio a scaricarle in una discussione con priorità BACKGROUND. Qualcosa di simile a questo: -

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

Il codice downloadMissingImages assomiglia a questo: -

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

Questo funziona, ma blocca il l'applicazione si blocca dopo un po 'utente principale e. Questo è quando cerco di scaricare circa 700 immagini. Con più immagini, sarebbe sicuramente in crash.

2) Quando un utente fa clic su una categoria, ho bisogno di scaricare quelle immagini per prime perché devono essere mostrate all'utente immediatamente. Non sono sicuro di come posso interrompere la chiamata missingImages e dirgli di iniziare a scaricare determinate immagini prima di altre.

Quindi, in sostanza, ho bisogno di scaricare tutte le immagini mancanti sullo sfondo, ma se l'utente sta navigando in una categoria di foto, quelle immagini devono avere la massima priorità nella coda di download.

Non riesco a capire come farlo funzionare in modo efficiente. qualche idea?

log

il crollo simile a questa

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) 

Grazie in anticipo.

risposta

3

per la caduta, credo che la vostra applicazione è ucciso a causa di una delle due opzioni:

  1. l'applicazione diventa non risponde (e quindi non risponde al processo di Sentinel iOS);

  2. troppa memoria utilizzata nel ciclo che crea oltre 700 operazioni di richiesta.

Per chiarire cosa sta realmente accadendo, è necessario fornire maggiori informazioni sul crash (il log della console). In ogni caso, la correzione sarebbe il caricamento delle immagini in blocchi di forse 10 o 20 ciascuno (si potrebbe andare anche 1 per 1, se ti piace, non vedo molti problemi con quello).

Circa il secondo punto, che dire questo:

  1. scaricare un'immagine priorità maggiore nel thread principale (tramite una scarica asincrona, naturalmente, per evitare il blocco della UI);

  2. prima di iniziare a scaricare un'immagine "offline", si controlla se l'immagine è già stata scaricata nel frattempo attraverso un download "a priorità più alta".

Per gestire punto 2 bene, si sarebbe probabilmente bisogno di fare la fila l'operazione personalizzato invece di un AFImageRequestOperation al fine di fare il controllo prima che il download vero e proprio.

EDIT:

A proposito di scaricare le immagini a pezzo, quello che si potrebbe fare è usare i gruppi di spedizione per raggruppare le operazioni di rete:

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); 

In questo esempio, sto usando un gruppo di spedizione per raggruppare un numero di operazioni asincrone e attendere che vengano eseguite tutte; quando dispatch_group_wait restituisce, è possibile eseguire un altro giro di quella (interrogando i dati principali e le operazioni di dispacciamento).

Informazioni sull'altra domanda (come è possibile verificare se una coda con priorità più alta ha già scaricato una determinata immagine), è necessario eseguire una query dei dati di base prima di eseguire ogni AFImageRequestOperation; una possibilità è derivare dalla tua classe e sovrascrivere il metodo start per eseguire il controllo.

In entrambi gli account, è possibile semplificare molto le logiche di tutto ciò scaricando le immagini una alla volta (ovvero, il ciclo for (...) non ci sarà, basta interrogare l'immagine successiva per scaricarla e scaricarla, prima di scaricare si controlla che non è già presente.

io vi suggerisco di andare su questa strada più facile.

Speranza che aiuta.

+0

Ciao Sergio, hai qualche esempio di codice per dimostrare download Chunked come suggerisce? –

+0

anche , come posso verificare se una coda con priorità più alta ha già scaricato un ce ottieni l'immagine? Qualche esempio di codice per questo? –

+0

Ho modificato la domanda per includere i registri degli arresti anomali - I thumbnails generati da ogni download di immagini .. quindi immagino che 700 immagini causino sicuramente la perdita di memoria qui .. –