iPhone - dequeueReusableCellWithIdentifier usage

IphoneObjective COptimizationUitableview

Iphone Problem Overview


I'm working on a iPhone app which has a pretty large UITableView with data taken from the web, so I'm trying to optimize its creation and usage.

I found out that dequeueReusableCellWithIdentifier is pretty useful, but after seeing many source codes using this, I'm wondering if the usage I make of this function is the good one.

Here is what people usually do:

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

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

// Add elements to the cell
return cell;

And here is the way I did it:

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

The difference is that people use the same identifier for every cell, so dequeuing one only avoids to alloc a new one.

For me, the point of queuing was to give each cell a unique identifier, so when the app asks for a cell it already displayed, neither allocation nor element adding have to be done.

In fine I don't know which is best, the "common" method ceils the table's memory usage to the exact number of cells it display, whilst the method I use seems to favour speed as it keeps all calculated cells, but can cause large memory consumption (unless there's an inner limit to the queue).

Am I wrong to use it this way? Or is it just up to the developer, depending on his needs?

Iphone Solutions


Solution 1 - Iphone

The purpose of dequeueReusableCellWithIdentifier is to use less memory. If the screen can fit 4 or 5 table cells, then with reuse you only need to have 4 or 5 table cells allocated in memory even if the table has 1000 entries.

In the second way there is no reuse. There is no advantage in the second way over just using an array of table cells. If your table has 1000 entries then you will have 1000 cells allocated in memory. If you are going to do that you would put them in an array and just index the array with the row number and return the cell. For small tables with fixed cells that may be an reasonable solution, for dynamic or large tables it is not a good idea.

Solution 2 - Iphone

As for cell identifier- Instead of just using "cell" for the identifier, and instead of using a unique identifier like the OP, could you use a "type-identifier"? For example, if my table had 3 types of cells- one with a very complicated sub-layout, one with just Style1, and one with Style2, I should identify those three all separately and then just rebuild them if dequeue comes up nil.

For example:

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

(This code probably won't run because I wrote it here, but hopefully you get the idea. )

I don't think Apple would have created the whole reusable cell idea with identifiers if they wanted all the identifiers to be "cell", don't you think?

Solution 3 - Iphone

The documentation that helped me understand why the idiomatic way (the one you described first) works best was UITableViewCell class reference section on the initWithStyle:reuseIdentifier: method.

The reuseIdentifier subsection reads:

> You should use the same reuse identifier for all cells of the same form.

And the "Discussion" subsection reads:

> The reuse identifier is associated with those cells (rows) of a table view that have the same general configuration, minus cell content.

These statements make it clear to me that the idiomatic way to use dequeueReusableCellWithIdentifier inside of your implementation of tableView:cellForRowAtIndexPath: for your UITableViewDataSource creates one cell object for each visible row regardless of the total number of rows available.

Solution 4 - Iphone

I think the first one is the best (and as you said common) way to implement a UITableView. With your second way there will be memory allocated for every new cell which is displayed and no memory will be reused.

Solution 5 - Iphone

UITableView internally uses a cell with an identifier as a "Template". So the next time you (read as table) try to deque, it just creates a new cell but using the stored object as template. Hence you still have to update its UI to reflect the cell contents as per context.

This also means that the UITableView is doing the memory management of the cells for us, per se. In theory, there will be only so many UITableViewCell objects as many as the visible cells. But practically, there might be a couple more waiting to be memory released.

This basically saves memory big time, esp in scenarios where you have 1000 cells.

On any portable device where memory is at a premium, we should defer the allocation of any memory to the last possible moment and release it the moment its job is done. dequeAndReusing a cell achieves this and does it pretty well.

On the other hand, if your cell is a customized cell, then we might most probably load a nib and extract from it. If this is the case, you can either use an identifier to deque OR you can load it from the nib. There is no difference in the procedure.

The only difference could be in the load time. Allowing the Table view to create a new cell using the identifier cell as template could be slightly faster than loading from nib but it is hardly noticeable and depends on the context.

Solution 6 - Iphone

To distinguish cell from other cells you can use tag property of the cell or if you are using the custom cell then its very easy through introducing any new property to custom cell while subclassing UITableViewCell.

Even though after all these you are stuck and still need to get cell, then you can try following code

UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath]

whereas it should be avoided upto extent since it produces the copy of cell but do not return the existing cell whereas the contents will be of same values.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJukurrpaView Question on Stackoverflow
Solution 1 - IphoneprogrmrView Answer on Stackoverflow
Solution 2 - IphoneTimView Answer on Stackoverflow
Solution 3 - IphoneJeffView Answer on Stackoverflow
Solution 4 - IphoneAlexVogelView Answer on Stackoverflow
Solution 5 - IphoneDeepak G MView Answer on Stackoverflow
Solution 6 - IphoneNikita Sharma SahuView Answer on Stackoverflow