2016-03-14 13 views
6

ich this question gelesen haben und denke, dass ich den Unterschied zwischen den beiden Methoden verstehen, bis ich ein seltsames Beispiel finden:dequeueReusableCellWithIdentifier: forIndexPath: VS dequeueReusableCellWithIdentifier:

Set Tisch Stil-Sichtzelle Grund sein, Identifier sein Zelle wie unten in der Storyboard, Code:

import UIKit 

class TableViewController: UITableViewController { 
    var items: [String]! 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     items = ["first", "second", "third"] 
    } 

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 
     return 1 
    } 

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     return items.count 
    } 

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
     // either works fine 
     let cell = tableView.dequeueReusableCellWithIdentifier("Cell")! // let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) 

     cell.textLabel?.text = items[indexPath.row] 
     return cell 
    } 
} 

enter image description here

Sehr einfach, aber wenn ich ändere die tableView:cellForRowAtIndexPath: Methode 1, 2, 3, 4 Fälle jeweils:

Fall 1:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) 
    cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) 

    cell.textLabel?.text = items[indexPath.row] 
    return cell 
} 

Fall 2:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) 
    cell = tableView.dequeueReusableCellWithIdentifier("Cell")! 

    cell.textLabel?.text = items[indexPath.row] 
    return cell 
} 

Fall 3:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    var cell = tableView.dequeueReusableCellWithIdentifier("Cell")! 
    cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) 

    cell.textLabel?.text = items[indexPath.row] 
    return cell 
} 

Fall 4:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    var cell = tableView.dequeueReusableCellWithIdentifier("Cell")! 
    cell = tableView.dequeueReusableCellWithIdentifier("Cell")! 

    cell.textLabel?.text = items[indexPath.row] 
    return cell 
} 

Fall, 2 (nicht funktioniert):

enter image description here

Fall 3, 4 (funktioniert):

enter image description here

Wie zu erklären? Ich denke, es hilft wirklich, diese beiden Methoden aus einer anderen Perspektive zu verstehen, jede Meinung ist willkommen.

Antwort

6

In jeder Fall, du a entheben zwei Zellen für jede Zeile. In den Fällen 1 und 2 rufen Sie zuerst die ("Cell", forIndexPath: indexPath)-Version auf.In diesem Fall endet die Tabellenansicht mit zwei Zellen für jede Zeile, eine vollständig überlappend und die andere überdeckend. Sie können dies in der Ansicht Inspektoren sehen, da Sie den Blickwinkel ändern können hinter sehen:

enter image description here

(I geändert, um den cellForRowAtIndexPath Code wie folgt:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    var cell = tableView.dequeueReusableCellWithIdentifier("plainCell", forIndexPath: indexPath) 
    cell.textLabel!.text = "First cell for row \(indexPath.row)" 
    cell = tableView.dequeueReusableCellWithIdentifier("plainCell", forIndexPath: indexPath) 
    cell.textLabel!.text = "Second cell for row \(indexPath.row)" 
    print("Cell being returned is \(cell)") 
    return cell 
} 

zu bestimmten verschiedenen Beschriftungen zu jeder Zelle.) In den Fällen 3 und 4, wo Sie zuerst die ("Cell") Version aufrufen, hat die Tabellenansicht nur eine Zelle für jede Zeile.

Warum das unterschiedliche Verhalten? Wenn Sie eine benutzerdefinierte Unterklasse von UITableViewCell erstellen und diese in Ihrem Storyboard verwenden, können Sie verschiedene Methoden überschreiben und print() Anweisungen hinzufügen, um zu sehen, was passiert. Insbesondere awakeFromNib, didMoveToSuperView und deinit. Was sich ergibt, ist, dass in den Fällen 1 und 2 die erste Zelle erstellt wird (watchFromNib) und sofort (didMoveToSuperView) zu einer Superview hinzugefügt wird, vermutlich die Tabellenansicht oder eine ihrer Unteransichten. In den Fällen 3 und 4 wird die erste Zelle erstellt, aber keiner Superview hinzugefügt. Stattdessen wird die Zelle einige Zeit später freigegeben (Deinit).

(Beachten Sie, dass, wenn die zweite Zelle aus der Warteschlange entfernt wird unter Verwendung der ("Cell", forIndexPath: indexPath) Version auch sofort zu einem Superview hinzugefügt wird. Wenn jedoch die zweite Zelle aus der Warteschlange entfernt wird unter Verwendung der ("Cell") Version wird es nur zu einer Super nach dem cellForRowAtIndexPath Methode hat zurückgegeben.)

So ist der wesentliche Unterschied ist, dass die ("Cell", forIndexPath: indexPath) Version Ergebnisse in der Zelle sofort in der Tabellenansicht hinzugefügt werden, bevor auch die cellForRowAtIndexPath abgeschlossen hat. Dies ist in der Frage/Antwort angedeutet, auf die Sie verweisen, da es anzeigt, dass die aus der Warteschlange genommene Zelle die richtige Größe hat.

Sobald die Superzelle dem Superview hinzugefügt wurde, kann die erste Zelle nicht mehr freigegeben werden, da es immer noch einen starken Verweis auf ihre Superansicht gibt. Wenn die Zellen mit der ("Cell")-Version aus der Warteschlange entfernt werden, werden sie dem Superview nicht hinzugefügt, daher gibt es keinen starken Verweis auf sie, sobald die cell-Variable neu zugewiesen wird und sie folglich freigegeben werden.

Hoffe, dass alles einen Sinn ergibt.

+0

Ihre Antwort ist fantastisch, vielen Dank! – fujianjin6471

1

dequeueReusableCellWithIdentifier: gibt Ihnen keine Garantien: Zellen könnten nil sein, also müssen Sie überprüfen, ob Ihre Zelle nil ist und es richtig handhaben oder Ihre App wird abstürzen.

, auf der anderen Seite, überprüft dies für Sie (es immer eine Zelle zurückgeben).

Für Ihren speziellen Fall (Swift) bedeutet dies, dass Sie die Zelle sicher mit erzwingen können, während Sie die if let Syntax mit der zweiten verwenden müssen.

Beispiel Codes (in Objective-C, ich verwende Swift nicht)

dequeueReusableCellWithIdentifier: forIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" atIndexPath:indexPath]; 

    // Here we know the cell is not nil (....atIndexPath: ensures it) 
    cell.textLabel.text = items[indexPath.row]; 

    return cell; 
} 

dequeueReusableCellWithIdentifier:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; 

    // You asked for a cell, but you don't know if it is nil or not 
    // In Swift, here the cell should be a conditional 

    // First, check if the cell is nil 
    if (cell == nil) { 
     // Cell is nil. To avoid crashes, we instantiate an actual cell 
     // With Swift conditional should be something similar 
     cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"]; 
    } 

    // Here you're sure the cell is not nil 
    // If condicional, you probably will write cell?.textLabel?.text = items[indexPath.row]; 
    cell.textLabel.text = items[indexPath.row]; 

    // Finally, you return the cell which you're 100% sure it's not nil 
    return cell; 
} 
+0

Warum funktioniert Fall 1, 2 nicht wie erwartet? – fujianjin6471

+0

Nicht sicher (Ich mag Swift nicht, also habe ich nicht zu viel davon), aber es sieht so aus, als wäre dein 'textLabel' - Attribut kein bedingtes, sondern ein tatsächliches' UITextField', also versuche es auszupacken ('?') wird fehlschlagen, da es ein tatsächliches Objekt und kein Wrapper ist. Sie können sehen, dass die eigentliche Zelle ** da ist **, weil sonst Ihre App abstürzen würde (Rückgabe von 'nil' in' tableView: cellForRowAtIndexPath: 'löst eine Exception aus und stürzt bei der Rückgabe von' nil' ab). Versuchen Sie 'cell.textLabel.text' ohne das'? 'Oder eine Mischung aus bedingten und erzwungenen Unwrapping ('? 'Und'! '). –

+0

Danke, aber ich denke, was sinnvoll ist, ist nicht die Sprache, die wir verwenden. @ pbasdf's Antwort erleuchtet mich, es hilft wirklich – fujianjin6471