it-swarm.com.de

iPhone - dequeueReusableCellWithIdentifier Verwendung

Ich arbeite an einer iPhone-App, die über ein ziemlich großes UITableView mit Daten aus dem Web verfügt. Daher versuche ich, deren Erstellung und Verwendung zu optimieren.

Ich habe herausgefunden, dass dequeueReusableCellWithIdentifier ziemlich nützlich ist, aber nachdem ich viele Quellcodes mit diesem Code gesehen habe, frage ich mich, ob die Verwendung dieser Funktion die gute ist.

Folgendes tun die Leute normalerweise:

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

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

// Add elements to the cell
return cell;

Und so habe ich es gemacht:

// 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 Personen für jede Zelle denselben Bezeichner verwenden. Wenn Sie also einen Eintrag in die Warteschlange stellen, wird nur die Zuweisung eines neuen Kennzeichens vermieden.

Für mich bestand der Punkt der Warteschlange darin, jeder Zelle einen eindeutigen Bezeichner zu geben. Wenn also die App nach einer Zelle fragt, die bereits angezeigt wurde, müssen weder Zuordnungen noch Elemente hinzugefügt werden.

In Ordnung Ich weiß nicht, was am besten ist, die "allgemeine" Methode begrenzt die Speicherbelegung der Tabelle auf die genaue Anzahl der angezeigten Zellen, während die von mir verwendete Methode die Geschwindigkeit zu bevorzugen scheint, da sie alle berechneten Zellen speichert, jedoch zu einem hohen Speicherbedarf führt (es sei denn, es gibt eine innere Grenze für die Warteschlange).

Bin ich falsch, es auf diese Weise zu benutzen? Oder liegt es nur am Entwickler, abhängig von seinen Bedürfnissen?

61
Jukurrpa

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

Zweitens gibt es keine Wiederverwendung. Auf dem zweiten Weg gibt es keinen Vorteil gegenüber der Verwendung eines Arrays von Tabellenzellen. Wenn Ihre Tabelle 1000 Einträge enthält, werden 1000 Zellen im Speicher zugewiesen. Wenn Sie das tun, setzen Sie sie in ein Array und indizieren das Array mit der Zeilennummer und geben die Zelle zurück. Für kleine Tabellen mit festen Zellen, die eine sinnvolle Lösung darstellen können, ist es für dynamische oder große Tabellen keine gute Idee.

70
progrmr

Wie bei der Zellenkennung: Statt nur "Zelle" für die Kennung zu verwenden und anstelle einer eindeutigen Kennung wie dem OP eine "Typenbezeichnung" zu verwenden? Wenn meine Tabelle beispielsweise 3 Arten von Zellen hatte - eine mit einem sehr komplizierten Unterlayout, eine mit nur Style1 und eine mit Style2 -, sollte ich diese drei einzeln identifizieren und sie dann neu aufbauen, wenn dequeue nil erscheint.

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 ausgeführt, weil ich ihn hier geschrieben habe, aber hoffentlich haben Sie die Idee.) 

Ich glaube nicht, dass Apple die gesamte wiederverwendbare Zellenidee mit Bezeichnern erstellt hätte, wenn alle Bezeichner "cell" sein wollten, finden Sie nicht?

19
Tim

Die Dokumentation, die mir half zu verstehen, warum der idiomatische Weg (der von Ihnen zuerst beschriebene) am besten funktioniert, war UITableViewCell-Klassenreferenz - Abschnitt über die initWithStyle:reuseIdentifier:-Methode.

Der reuseIdentifier-Unterabschnitt lautet:

Sie sollten denselben Wiederverwendungsbezeichner für alle Zellen desselben Formulars verwenden.

Und der Unterabschnitt "Diskussion" lautet:

Der Wiederverwendungsbezeichner ist den Zellen (Zeilen) einer Tabellensicht zugeordnet, die dieselbe allgemeine Konfiguration haben, abzüglich des Zellinhalts.

Diese Aussagen machen mir klar, dass die idiomatische Verwendung von dequeueReusableCellWithIdentifier in Ihrer Implementierung von tableView:cellForRowAtIndexPath: für Ihre UITableViewDataSource ein Zellenobjekt für jede visible - Zeile unabhängig von der Gesamtzahl der verfügbaren Zeilen erstellt.

13
Jeff

Ich denke, der erste ist der beste (und wie Sie gesagt haben übliche) Weg, um eine UITableView..__ zu implementieren. Bei Ihrem zweiten Weg wird für jede neue Zelle Speicherplatz zugewiesen, der angezeigt wird, und es wird kein Speicher mehr verwendet.

4
AlexVogel

UITableView verwendet intern eine Zelle mit einem Bezeichner als "Vorlage". Wenn Sie das nächste Mal (als Tabelle lesen) versuchen, die Datei zu entpacken, wird lediglich eine neue Zelle erstellt, das gespeicherte Objekt wird jedoch als Vorlage verwendet. Daher müssen Sie die Benutzeroberfläche immer noch aktualisieren, um den Zellinhalt gemäß dem Kontext wiederzugeben.

Dies bedeutet auch, dass UITableView die Speicherverwaltung der Zellen für uns selbst übernimmt. Theoretisch gibt es nur so viele UITableViewCell Objekte wie die sichtbaren Zellen. Aber praktisch könnte es noch ein paar mehr geben, die darauf warten, dass die Erinnerung freigegeben wird.

Dies spart im Grunde genommen viel Speicherplatz, insbesondere in Szenarien mit 1000 Zellen.

Auf jedem tragbaren Gerät, auf dem Speicher knapp ist, sollten wir die Zuordnung eines Speichers auf den letztmöglichen Zeitpunkt verschieben und ihn freigeben, sobald seine Arbeit erledigt ist. dequeAndReusing eine Zelle schafft das und macht es ziemlich gut.

Wenn es sich bei Ihrer Zelle hingegen um eine benutzerdefinierte Zelle handelt, wird höchstwahrscheinlich eine Schreibfeder geladen und daraus extrahiert. In diesem Fall können Sie entweder einen Bezeichner verwenden, um OR zu dequen, und Sie können ihn von der Schreibfeder laden. Es gibt keinen Unterschied in der Vorgehensweise.

Der einzige Unterschied könnte in der Ladezeit liegen. Wenn Sie der Tabellenansicht erlauben, eine neue Zelle unter Verwendung der Bezeichnerzelle als Vorlage zu erstellen könnte etwas schneller sein als das Laden von der Nib, aber es ist kaum wahrnehmbar und hängt vom Kontext ab.

1
Deepak G M

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

Auch wenn Sie nach all dem festgefahren sind und immer noch Zellen benötigen, können Sie folgenden Code ausprobieren

UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath]

es sollte jedoch bis zu einem gewissen Grad vermieden werden, da es die Kopie der Zelle erzeugt, die vorhandene Zelle jedoch nicht zurückgibt, während der Inhalt die gleichen Werte hat.

0