2010-05-28 9 views
60

Sto lavorando ad un'app per iPhone che ha un UITableView piuttosto grande con dati presi dal web, quindi sto cercando di ottimizzarne la creazione e l'utilizzo.iPhone - dequeueReusableCellWithIdentificatore di utilizzo

Ho scoperto che dequeueReusableCellWithIdentifier è piuttosto utile, ma dopo aver visto molti codici sorgente usando questo, mi chiedo se l'utilizzo che faccio di questa funzione sia il migliore.

Ecco quello che la gente di solito:

UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; 

if (cell == nil) { 
    cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Cell"]; 

// Add elements to the cell 
return cell; 

E qui è il modo in cui l'ho fatto:

// The cell row 
NSString identifier = [NSString stringWithFormat:@"Cell %d", indexPath.row]; 

UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:identifier]; 

if (cell != nil) 
    return cell; 

cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:identifier]; 
// Add elements to the cell 
return cell; 

La differenza è che le persone utilizzano lo stesso identificatore per ogni cellula, così l'accodamento uno evita solo di assegnarne uno nuovo.

Per me, il punto di accodamento era di assegnare a ciascuna cella un identificatore univoco, quindi quando l'app richiede una cella già visualizzata, non è necessario eseguire né l'allocazione né l'aggiunta di elementi.

Con il bel Non so quale sia il migliore, il metodo del "comune" ceils utilizzo della memoria della tabella per il numero esatto delle cellule esso esposizione, whislt il metodo che uso sembra favorire la velocità in quanto mantiene tutto calcolato celle, ma può causare un notevole consumo di memoria (a meno che non ci sia un limite interno alla coda).

Ho sbagliato a usarlo in questo modo? O dipende solo dallo sviluppatore, a seconda delle sue esigenze?

risposta

70

Lo scopo di dequeueReusableCellWithIdentifier è di utilizzare meno memoria. Se lo schermo può contenere 4 o 5 celle di tabella, quindi con il riutilizzo è necessario avere solo 4 o 5 celle di tabella allocate in memoria anche se la tabella ha 1000 voci.

Nel secondo modo non c'è riutilizzo. Non vi è alcun vantaggio nel secondo modo rispetto all'utilizzo di un array di celle di tabella. Se la tua tabella ha 1000 voci, avrai 1000 celle allocate in memoria. Se lo farai, li inserirai in un array e indicizzerai semplicemente l'array con il numero di riga e restituirai la cella. Per i tavoli di piccole dimensioni con celle fisse che possono essere una soluzione ragionevole, per tavoli dinamici o di grandi dimensioni non è una buona idea.

+0

Hai ragione sul fatto che con il mio metodo un array potrebbe fare il lavoro. Le ~ 100 celle rappresentano una quantità troppo grande di memoria allocata contemporaneamente? – Jukurrpa

+0

Bene, ho cambiato per il metodo comune (che è complicato quando una riga ha molte sottoview), e oltre al minor consumo di memoria, lo scorrimento generale sembra più agevole, non sono sicuro del perché. Grazie comunque per il consiglio! – Jukurrpa

+0

@progrmr ... grazie ... per chiarire il concetto .. è davvero bello ... ho fatto anche lo stesso errore .. :) –

4

Penso che il primo sia il modo migliore (e come hai detto in comune) per implementare uno UITableView. Con la tua seconda via ci sarà la memoria allocata per ogni nuova cella che viene visualizzata e nessuna memoria verrà riutilizzata.

+0

La memoria di pozzo viene riutilizzata se l'utente scorre verso il basso e quindi scorre indietro ... O ogni volta che tableView accede a una cella che è stata visualizzata una volta e poi nascosta. – Jukurrpa

+0

Certo, ma ci sarà un footprint di memoria per ogni cella che sia mai stata visualizzata e non solo per la cella 4-5 attualmente visualizzata. Ho avuto un problema simile con le annotazioni per la mappa. Passare a un identificatore costante ha comportato un notevole aumento delle prestazioni. – AlexVogel

+0

Quindi suppongo che tocca a me ... Sarebbe meglio memorizzare nella cache tutti i dati che uso per creare la cella piuttosto che la cella stessa? – Jukurrpa

20

Come per l'identificatore di cella- Invece di utilizzare semplicemente "cella" per l'identificatore e invece di utilizzare un identificatore univoco come l'OP, è possibile utilizzare un "identificatore di tipo"? Ad esempio, se il mio tavolo avesse 3 tipi di celle, uno con un sotto-layout molto complicato, uno con solo Style1 e uno con Style2, dovrei identificare tutti e tre tutti separatamente e quindi ricostruirli solo se viene visualizzato il dequeue nil.

Ad esempio:

-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath{ 
    NSString* ident = @""; 
    if(indexPath.section == 0) ident= @"complicated"; 
    if(indexPath.section == 1) ident= @"style1"; 
    if(indexPath.section == 2) ident = @"style2"; 

    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:ident]; 

    if(cell == nil){ 

     if(ident == @"complicated"){ 
      cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:ident] autorelease]; 
     // do excessive subview building 
     } 
     if(ident == @"style1"){ 
      cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyle1 reuseIdentifier:ident] autorelease]; 
     } 

     if(ident == @"style2"){ 
      cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyle2 reuseIdentifier:ident] autorelease]; 
     } 


    } 
    if(ident == @"complicated"){ 
     // change the text/etc (unique values) of our many subviews 
    } 
    if(ident == @"style1"){ 
     [[cell textLabel] setText:@"Whatever"]; 
    } 
    if(ident == @"style2"){ 
     [[cell textLabel] setText:@"Whateverelse"]; 
    } 

    return cell; 
} 

(Questo codice probabilmente non verrà eseguito perché l'ho scritto qui, ma spero che si ottiene l'idea.)

Non credo che Apple avrebbe creato il tutta l'idea di cella riutilizzabile con identificatori se volevano che tutti gli identificatori fossero "cell", non credi?

+0

Nel tuo caso utilizzerei un identificatore per ogni layout di cella. Il che significa uno per le tue celle "complicate", uno per le celle "style1" e forse un terzo per le celle style2, se le loro sottoview differiscono da quelle style1. Nel caso in cui la dequeue ritorni nil, aggiungi la sottoview della cella con i tag (definiti in un enum o qualcosa del genere), quindi inizializzali. Nel caso in cui dequeue restituisca una cella, recupera semplicemente le sottoview usando i tag e cambiali. Separare il codice in un metodo per ogni sezione sarebbe bello :) – Jukurrpa

+0

Questa è una buona risposta. Ho votato. –

13

La documentazione che mi ha aiutato a capire perché il modo più idiomatico (quello che hai descritto per primo) funziona meglio era la sezione UITableViewCell class reference sul metodo initWithStyle:reuseIdentifier:.

Il reuseIdentifier comma recita:

si dovrebbe usare lo stesso identificatore riutilizzo per tutte le celle della stessa forma.

E la sottosezione "Discussione" si legge:

L'identificatore riuso è associata a quelle cellule (righe) di una vista tabella che hanno la stessa configurazione generale, meno il contenuto delle celle.

Queste dichiarazioni rendono chiaro per me che il modo idiomatico di usare dequeueReusableCellWithIdentifier all'interno della vostra implementazione di tableView:cellForRowAtIndexPath: per il vostro UITableViewDataSource crea un oggetto cella per ogni riga visibile indipendentemente dal numero totale di righe disponibili.

1

UITableView utilizza internamente una cella con un identificatore come "Modello". Quindi la prossima volta che (leggi come tabella) prova a deque, crea solo una nuova cella ma usa l'oggetto memorizzato come modello. Quindi devi ancora aggiornare la sua interfaccia utente per riflettere il contenuto della cella come da contesto.

Ciò significa anche che lo UITableView sta gestendo la memoria delle celle per noi. In teoria, ci saranno solo così tanti oggetti UITableViewCell come le celle visibili. Ma praticamente, ci potrebbe essere un altro paio in attesa di essere liberato dalla memoria.

Questo fondamentalmente fa risparmiare molto tempo alla memoria, specialmente negli scenari in cui si hanno 1000 celle.

Su qualsiasi dispositivo portatile in cui la memoria è limitata, dovremmo rimandare l'allocazione di qualsiasi memoria all'ultimo momento possibile e rilasciarla nel momento in cui è stata eseguita. dequeAndReusing una cella raggiunge questo e lo fa piuttosto bene.

D'altra parte, se la tua cella è una cella personalizzata, probabilmente potremmo caricare un pennino ed estrarlo da esso. Se questo è il caso, è possibile utilizzare un identificatore per deque OPPURE è possibile caricarlo dal pennino. Non c'è differenza nella procedura.

L'unica differenza potrebbe essere nel tempo di caricamento. Consentire la visualizzazione Tabella per creare una nuova cella utilizzando la cella identificatore come modello potrebbe essere leggermente più veloce del caricamento dal pennino ma è difficilmente apprezzabile e dipende dal contesto.

+0

Le celle personalizzate sono gestite anche dal sistema di code di memoria di tableView, purché abbiano un set di 'Identifier'. – Tim

0

Per distinguere la cella da altre celle è possibile utilizzare la proprietà tag della cella o se si sta utilizzando la cella personalizzata, quindi è molto semplice introducendo una nuova proprietà alla cella personalizzata durante la sottoclasse UITableViewCell.

Anche se dopo tutti questi si sono bloccati e ancora bisogno di ottenere cellule, allora si può provare seguente codice

UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath]

considerando che dovrebbe essere evitato fino a misura in quanto produce la copia della cella, ma fare non restituisce la cella esistente mentre il contenuto sarà degli stessi valori.

Problemi correlati