DequeueReusableCellWithIdentifier: forIndexPath: VS dequeueReusableCellWithIdentifier:

I read this question and I think that I understand the difference between these two methods until I find a strange example:

Set the cell style of the table table to Main , the identifier is the cell in the storyboard, the code below:

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

Very simple, but when I change the tableView:cellForRowAtIndexPath: method to 1, 2, 3, 4 cases, respectively:

Case 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 } 

Case 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 } 

Case 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 } 

Case 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 } 

Case 1, 2 (does not work):

enter image description here

Case 3, 4 (works fine):

enter image description here

How to explain? I think it really helps to understand these two methods from a different perspective, any opinion is welcome.

+6
source share
2 answers

In each case, you delete two cells for each row. In cases 1 and 2, you first call the version ("Cell", forIndexPath: indexPath) . In this case, the table view ends with two cells for each row, one completely overlapping and obscuring the other. You can see this in the view inspector, as you can change the viewing angle to see behind:

enter image description here

(I changed the cellForRowAtIndexPath code as follows:

 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 } 

to give different text labels to each cell.) In cases 3 and 4, when you first call the version ("Cell") , there is only one cell for each row in the table view.

Why different behavior? If you create your own subclass of UITableViewCell and use it in your storyboard, you can override the various methods and add print() statements to see what happens. In particular, awakeFromNib , didMoveToSuperView and deinit . What happens is that in cases 1 and 2, the first cell is created (awakeFromNib) and immediately added (didMoveToSuperView) to the supervisor, presumably as a table or in one of its subzones. In cases 3 and 4, the first cell is created, but not added to the supervisor. Instead, after some time, the cell is freed (deinit).

(Note that if the second cell is canceled using the version ("Cell", forIndexPath: indexPath) , it is also added immediately to the supervisor. However, if the second cell is deleted using the version ("Cell") , it is only added to the supervisor after return the cellForRowAtIndexPath method.)

Thus, the key difference is that the version ("Cell", forIndexPath: indexPath) causes the cell to be immediately added to the table view before even cellForRowAtIndexPath . This hints at the question / answer you are referring to, as this indicates that the selected cell will have the correct size.

After being added to the supervisor, the first cell cannot be freed because there is still a strong link to it from its supervisor. If cells are canceled using the version ("Cell") , they are not added to the supervisor, therefore, there is no strong reference to them after the cell variable is redistributed, and therefore they are freed.

Hope this all makes sense.

+6
source

dequeueReusableCellWithIdentifier: does not give you guarantees: the cells may be nil , so you need to check if your cell is nil and process it properly or your application crashes.

dequeueReusableCellWithIdentifier:forIndexPath: on the other hand, checks this for you (it always returns a cell).

For your specific case (Swift), this means that you can safely disconnect the cell using dequeueReusableCellWithIdentifier:forIndexPath: while you need to use the if let syntax with the second.

Code examples (in Objective-C, I do not use Swift)

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 not nil return cell; } 
+1
source

All Articles