2010-02-13 8 views
10

Sto creando un'app per iPhone che estrae i dati da un'API Web, inclusi gli indirizzi email. Mi piacerebbe visualizzare un'immagine associata a ciascun indirizzo email nelle celle della tabella, quindi sto cercando nella rubrica le immagini e ricomincio da un valore predefinito se l'indirizzo email non è nel libro. Questa grande opera, ma ho alcune preoccupazioni:Qual è l'approccio migliore alla cache di immagini asincrone su iPhone?

  • prestazioni: Le ricette che ho trovato per la ricerca di un indirizzo libretto per indirizzo e-mail (o il numero di telefono) sono riferito rather slow. La ragione di questo è che si deve scorrere su ogni record della rubrica, e per ognuno che ha un'immagine, iterare su tutti gli indirizzi e-mail per trovare una corrispondenza. Questo può richiedere molto tempo per una grande rubrica, naturalmente.

  • celle di tabella: Così ho pensato di raccogliere tutti gli indirizzi email per cui ho bisogno di trovare le immagini e trovare tutti in una volta. In questo modo, faccio scorrere il libro solo una volta per tutti gli indirizzi. Ma questo non funziona bene per le celle di una tabella, dove ogni cella corrisponde a un singolo indirizzo email. Dovrei raccogliere tutte le immagini prima di visualizzare qualsiasi cella (potenzialmente lenta), o fare in modo che ogni cella cerchi ogni immagine mentre carica (anche più lentamente, poiché dovrei scorrere il libro per trovare una corrispondenza per ogni indirizzo e-mail).

  • Asynchronous Lookup: Allora ho pensato che li avrei lo sguardo alla rinfusa, ma in modo asincrono, utilizzando NSInvocationOperation. Per ogni immagine trovata in AddressBook, salverei una miniatura nella sandbox dell'app. Quindi ogni cella può solo fare riferimento a questo file e mostrare il valore predefinito se non esiste (perché non è nel libro o non è stato ancora trovato). Se l'immagine viene in seguito trovata nella ricerca asincrona, la prossima volta che l'immagine deve essere visualizzata sembrerebbe apparire all'improvviso. Questo potrebbe funzionare bene per la rigenerazione periodica delle immagini (per quando le immagini sono state modificate nella rubrica, ad esempio). Ma poi per qualsiasi istanza della mia app, un'immagine potrebbe non apparire per un po '.

  • Asynchronous Tabella Cell Lookup: Idealmente, mi piacerebbe usare qualcosa di simile markjnet's asynchronous table cell updating aggiornare celle della tabella con un'immagine una volta che è stato scaricato. Ma per farlo funzionare, dovrei eseguire un lavoro di NSInvocationOperation per ogni cella come viene visualizzata e se l'icona memorizzata nella cache non è presente nella sandbox. Ma poi siamo tornati a iterare in modo inefficiente attraverso l'intera rubrica per ognuno di essi, e questo può essere un sacco di loro se hai appena scaricato un sacco di nuovi indirizzi email.

Quindi la mia domanda è: come fanno gli altri? Stavo armeggiando con Tweetie2, e sembra che aggiorni le celle della tabella visualizzate in modo asincrono. Presumo che stia inviando una richiesta HTTP separata per ogni immagine di cui ha bisogno. Se è così, immagino che la ricerca nella rubrica locale via e-mail non sia meno efficiente, quindi forse questo è l'approccio migliore? Non ti preoccupare dei problemi di prestazioni associati alla ricerca nella rubrica?

In tal caso, sta salvando un'immagine in miniatura nella sandbox l'approccio migliore alla memorizzazione nella cache? E se volessi creare un nuovo lavoro per aggiornare tutte le anteprime con eventuali cambiamenti nella rubrica dire una volta al giorno, qual è l'approccio migliore per farlo?

In che modo il resto di voi risolve questo tipo di problema? I suggerimenti sarebbero molto apprezzati!

risposta

2

Indipendentemente da quale strategia si utilizza per la memorizzazione nella cache effettivo di immagini, vorrei fare solo passaggio attraverso i dati della rubrica ogni volta che si ottiene un lotto di indirizzi e-mail, se possibile. (E sì, lo farei in modo asincrono.)

Creare un NSMutableDictionary che servirà da cache in memoria per i risultati di ricerca. Inizializza questo dizionario con ogni indirizzo email dal download come chiave, con un sentinella come valore di tale chiave (ad esempio [NSNull null]).

Successivamente, scorrere ogni ABRecordRef nella Rubrica, chiamando ABRecordCopyValue(record, kABPersonEmailProperty) e eseguendo il looping dei risultati in ogni ABMultiValue restituito. Se uno qualsiasi degli indirizzi e-mail sono chiavi nella cache, impostare [NSNumber numberWithInt:ABRecordGetRecordId(record)] come valore di tale chiave nel dizionario.

Utilizzando questo dizionario come indice di ricerca, è possibile ottenere rapidamente le immagini di ABRecordRefs solo per gli indirizzi di posta elettronica che si stanno visualizzando nella visualizzazione tabella data la posizione di scorrimento corrente dell'utente, come suggerito nella risposta di Hoopjones. È possibile aggiungere un listener di modifica della rubrica per invalidare la cache, attivare un'altra operazione di indicizzazione e quindi aggiornare la vista, se l'applicazione richiede quel livello di "aggiornamento".

+0

Grazie. Quindi, ho effettivamente implementato una soluzione che cerca un indirizzo alla volta, ma crea una miniatura e la salva nella directory dei documenti sandbox. E sì, cerca solo le immagini nella rubrica la prima volta che devono essere visualizzate.I * think * il caricamento dell'immagine cache dalla sandbox dovrebbe essere abbastanza veloce da non dover essere asincrona. Non ho bisogno del livello di attualità che descrivi, quindi seguirò come pianificare gli aggiornamenti regolari delle miniature memorizzate nella cache - e sì, eseguirò l'iterazione solo attraverso la rubrica una volta per tutte loro. – theory

+0

Solo curioso, come è possibile cercare un indirizzo alla volta, ma solo una volta scorrere la rubrica? – erikprice

2

Vorrei utilizzare l'ultimo metodo elencato (Ricerca cella tabella asincrona) ma cercare solo le immagini per i record correnti visualizzati.Sovraccarico i metodi UIScrollViewDelegate per scoprire quando un utente ha smesso di scorrere e quindi avvia solo le richieste per le celle visibili correnti.

Qualcosa di simile (questo è leggermente modificato da un tutorial che ho trovato sul web, che non riesco a trovare ora, le scuse per non citare l'autore):

- (void)loadContentForVisibleCells 
{ 
    NSArray *cells = [self.table visibleCells]; 
    [cells retain]; 
    for (int i = 0; i < [cells count]; i++) 
    { 
     // Go through each cell in the array and call its loadContent method if it responds to it. 
     AddressRecordTableCell *addressTableCell = (AddressRecordTableCell *)[[cells objectAtIndex: i] retain]; 
     [addressTableCell loadImage]; 
     [addressTableCell release]; 
     addressTableCell = nil; 
    } 
    [cells release]; 
} 


- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView; 
{ 
    // Method is called when the decelerating comes to a stop. 
    // Pass visible cells to the cell loading function. If possible change 
    // scrollView to a pointer to your table cell to avoid compiler warnings 
    [self loadContentForVisibleCells]; 
} 


- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate; 
{ 
    if (!decelerate) 
    { 
     [self loadContentForVisibleCells]; 
    } 
} 

Una volta che sai quale indirizzo record sono attualmente visibile, solo facendo una ricerca per quelli (5 -7 record probabilmente) sarà veloce come un fulmine. Una volta catturata l'immagine, è sufficiente memorizzarla in un dizionario in modo da non dover ripetere la richiesta per l'immagine in un secondo momento.

+0

Solo per curiosità, perché questo codice di esempio mantenere il NSArray di celle e poi ogni AddressRecordTableCell prima di lavorare con loro e poi rilasciandoli, in 'loadContentForVisibleCells'? Non vedo cosa potrebbe farli diventare dealloc durante l'esecuzione di quel metodo. – erikprice

+2

Hai ragione. Non ha bisogno di essere mantenuto. Ho scritto questo codice molto tempo fa mentre stavo imparando Cocoa, quindi c'è un po 'di noia :) – hoopjones

+0

Buono a sapersi su ['UIScrollViewDelegate'] (http://developer.apple.com/iphone/library/documentation/ UIKit/Reference/UIScrollViewDelegate_Protocol/Riferimento/UIScrollViewDelegate.html). Questo è ciò che anche l'esempio [LazyTableImages] (http://developer.apple.com/iphone/library/samplecode/LazyTableImages/index.html) collegato da yonel qui sotto. La cosa divertente è che Tweetie 2 non sembra utilizzare questo approccio. Quando scorro velocemente, posso vedere il caricamento delle immagini mentre scorre. Mi ha sempre infastidito il fatto che le app Apple non caricassero immagini durante lo scorrimento e Tweetie 2 sembra mostrare che non è necessario. – theory

1

Sembra che tu cerchi di implementare il caricamento di immagini pigri in UITableView. c'è un buon esempio da parte di Apple, sto riferimento qui: Lazy load images in UITableView

+0

Molte grazie per il collegamento all'esempio 'LazyTableImages'. Non ne ero consapevole e felice di averlo per studiare. – theory