2010-05-28 13 views
60

Ich arbeite an einer iPhone App, die eine ziemlich große UITableView mit Daten aus dem Internet hat, so versuche ich, seine Erstellung und Nutzung zu optimieren.iPhone - dequeueReusableCellWithIdentifier Verwendung

Ich fand heraus, dass dequeueReusableCellWithIdentifier ziemlich nützlich ist, aber nachdem ich viele Quellcodes mit diesem gesehen habe, frage ich mich, ob die Verwendung, die ich von dieser Funktion mache, die gute ist. Hier

ist, was die Menschen in der Regel tun:

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

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

// Add elements to the cell 
return cell; 

Und hier ist die Art, wie ich es tat:

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

Der Unterschied besteht darin, dass die Menschen die gleiche Kennung für jede Zelle zu verwenden, so ein Ausreihen vermeidet nur, eine neue zuzuweisen.

Für mich bestand der Zweck der Warteschlangen darin, jeder Zelle eine eindeutige Kennung zu geben. Wenn die App also nach einer bereits angezeigten Zelle fragt, müssen weder Zuweisungen noch Elemente hinzugefügt werden.

In Ordnung Ich weiß nicht, welche die beste ist, die "gemeinsame" Methode deckt die Speicherbelegung der Tabelle mit der genauen Anzahl der Zellen, die es anzeigt, scheint die Methode, die Geschwindigkeit zu bevorzugen scheint, da es alle berechnet Zellen, kann aber einen großen Speicherverbrauch verursachen (es sei denn, es gibt eine innere Grenze für die Warteschlange).

Bin ich falsch, es auf diese Weise zu verwenden? Oder liegt es nur an den Entwicklern, abhängig von seinen Bedürfnissen?

Antwort

70

Der Zweck von dequeueReusableCellWithIdentifier ist es, weniger Speicher zu verwenden. Wenn der Bildschirm 4 oder 5 Tabellenzellen enthalten kann, müssen bei der Wiederverwendung nur 4 oder 5 Tabellenzellen im Speicher reserviert werden, auch wenn die Tabelle 1000 Einträge enthält.

In der zweiten Möglichkeit gibt es keine Wiederverwendung. Es gibt keinen Vorteil in der zweiten Art, nur ein Array von Tabellenzellen zu verwenden. Wenn Ihre Tabelle 1000 Einträge enthält, werden Ihnen 1000 Speicherzellen zugewiesen. Wenn Sie das tun, würden Sie sie in ein Array einfügen und das Array einfach mit der Zeilennummer indizieren und die Zelle zurückgeben. Für kleine Tabellen mit festen Zellen, die eine vernünftige Lösung darstellen können, ist dies für dynamische oder große Tabellen keine gute Idee.

+0

Sie haben recht mit der Tatsache, dass mit meiner Methode ein Array die Arbeit machen könnte. Würden ~ 100 Zellen eine zu große Menge an Speicher auf einmal bereitstellen? – Jukurrpa

+0

Nun, ich änderte für die allgemeine Methode (was kompliziert ist, wenn eine Zeile viele Unteransichten hat), und neben dem geringeren Speicherverbrauch scheint Scrollen insgesamt glatter, ich bin mir nicht sicher warum. Danke für den Hinweis sowieso! – Jukurrpa

+0

@progrmr ... danke ... für die Klärung des Konzepts .. es ist wirklich nett ... Ich habe auch den gleichen Fehler gemacht .. :) –

4

Ich denke, die erste ist die beste (und wie Sie sagten, gemeinsame) Weg, um eine UITableView zu implementieren. Mit Ihrem zweiten Weg wird Speicher für jede neue Zelle zugewiesen, die angezeigt wird, und kein Speicher wird wiederverwendet.

+0

Well-Speicher wird wiederverwendet, wenn der Benutzer scrollt und dann zurück scrollt ... oder jederzeit greift tableView auf eine Zelle zu, die einmal angezeigt und dann ausgeblendet wurde. – Jukurrpa

+0

Sicher, aber es wird einen Speicherabdruck für jede Zelle geben, die jemals angezeigt wurde und nicht nur für die 4-5 Zelle, die gerade angezeigt wird. Ich hatte ein ähnliches Problem mit Anmerkungen für die Karte. Der Wechsel zu einer konstanten Kennung brachte eine merkliche Leistungssteigerung. – AlexVogel

+0

Dann denke ich, es liegt an mir ... Wäre es besser, irgendwelche Daten zu cachen, die ich verwende, um die Zelle anstatt die Zelle selbst zu erstellen? – Jukurrpa

20

Wie für Zell-ID - anstatt nur "Zelle" für den Bezeichner zu verwenden, und anstatt eine eindeutige Kennung wie das OP, könnten Sie eine "Typ-Kennung" verwenden? Zum Beispiel, wenn meine Tabelle 3 Arten von Zellen hatte - eine mit einem sehr komplizierten Sub-Layout, eine mit nur Style1, und eine mit Style2, sollte ich diese drei alle separat identifizieren und dann nur neu erstellen, wenn die Warteschlange nil auftaucht.

Zum Beispiel:

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

(Dieser Code wird wahrscheinlich nicht laufen, weil ich es hier schreiben, aber hoffentlich bekommen Sie die Idee.)

Das glaube ich nicht, dass Apple die erstellt hätte ganze wiederverwendbare Zellidee mit Bezeichnern, wenn sie alle Bezeichner haben wollten "cell", meinst du nicht?

+0

In Ihrem Fall würde ich eine Kennung für jedes Zellenlayout verwenden. Das bedeutet eine für Ihre "komplizierten" Zellen, eine für die "style1" -Zellen und vielleicht eine dritte für style2-Zellen, wenn sich ihre Untersichten von denen von style1 unterscheiden. Wenn die Dequeue-Funktion nil zurückgibt, fügen Sie die Unteransicht der Zelle mit Tags (definiert in einer Enumeration oder etwas) hinzu und initialisieren Sie sie anschließend. Wenn die Dequeue eine Zelle zurückgibt, rufen Sie Subviews einfach mit Tags ab und ändern sie. Trennen Sie Ihren Code in einer Methode für jeden Abschnitt wäre auch nett :) – Jukurrpa

+0

Dies ist eine gute Antwort. Ich habe es gewählt. –

13

Die Dokumentation, die mir geholfen hat, zu verstehen, warum der idiomatische Weg (der, den Sie zuerst beschrieben haben) am besten war UITableViewCell class reference Abschnitt auf der initWithStyle:reuseIdentifier: Methode.

Der reuseIdentifier Abschnitt lautet:

Sie sollten die gleichen Wiederverwendung Bezeichner für alle Zellen der gleichen Form verwendet werden.

Und der „Discussion“ Unterabschnitt lautet:

Die Wiederverwendung Kennung wird mit den Zellen assoziiert (Zeilen) eine Tabellendarstellung, die die gleiche allgemeine Konfiguration hat, minus Zellinhalt.

Diese Aussagen machen es mir klar, dass der idiomatische Weg dequeueReusableCellWithIdentifier innerhalb Ihrer Implementierung von tableView:cellForRowAtIndexPath: zu verwenden für Ihre UITableViewDataSource schafft eine Zelle-Objekt für jede sichtbar Zeile unabhängig von der Gesamtzahl der Zeilen zur Verfügung.

1

UITableView intern verwendet eine Zelle mit einer Kennung als "Vorlage". Wenn Sie also das nächste Mal (als Tabelle lesen) versuchen, zu deque, wird nur eine neue Zelle erstellt, aber das gespeicherte Objekt wird als Vorlage verwendet. Daher müssen Sie die Benutzeroberfläche immer noch aktualisieren, um den Zelleninhalt gemäß Kontext wiederzugeben.

Das bedeutet auch, dass die UITableView die Speicherverwaltung der Zellen für uns an sich erledigt. In der Theorie wird es nur so viele Objekte wie die sichtbaren Zellen geben. Aber praktisch könnte es ein paar mehr geben, die darauf warten, dass die Erinnerung freigegeben wird.

Dies spart im Grunde Speicher viel Zeit, vor allem in Szenarien, in denen Sie 1000 Zellen haben.

Auf jedem tragbaren Gerät, auf dem der Arbeitsspeicher knapp ist, sollten wir die Zuweisung von Speicher auf den letzten möglichen Zeitpunkt verschieben und ihn in dem Moment freigeben, in dem seine Arbeit erledigt ist. dequeAndReusing eine Zelle erreicht dies und macht es ziemlich gut.

Auf der anderen Seite, wenn Ihre Zelle eine benutzerdefinierte Zelle ist, dann können wir höchstwahrscheinlich eine Feder laden und davon extrahieren. Wenn dies der Fall ist, können Sie entweder eine ID zum Deque verwenden oder Sie können sie von der NIB laden. Es gibt keinen Unterschied in der Prozedur.

Der einzige Unterschied könnte in der Ladezeit sein. Zulassen, dass die Tabellenansicht eine neue Zelle mit der Bezeichnerzelle als Vorlage könnte etwas schneller als das Laden von Nib sein, aber es ist kaum bemerkbar und hängt vom Kontext ab.

+0

Benutzerdefinierte Zellen werden auch vom Speicherwarteschlangensystem von tableView verwaltet, sofern sie über einen 'Identifier'-Satz verfügen. – Tim

0

Um Zellen von anderen Zellen zu unterscheiden, können Sie die Tag-Eigenschaft der Zelle verwenden oder wenn Sie die benutzerdefinierte Zelle verwenden, dann ist es sehr einfach, eine neue Eigenschaft in die benutzerdefinierte Zelle einzuführen, während UITableViewCell.

Obwohl nachdem diese alles, was Sie stecken und müssen noch Zelle bekommen, dann können Sie versuchen, Code folgende

UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath]

während es bis gehend vermieden werden sollte, da es die Kopie der Zelle produziert aber tun Die vorhandene Zelle wird nicht zurückgegeben, während der Inhalt dieselben Werte aufweist.

Verwandte Themen