Why we use the cellIdentifier? - iphone

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier ];
if(cell == nil){
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
cell.text = [[views objectAtIndex:indexPath.row] objectForKey:#"title"];
return cell;
}
Why we use the CellIdentifier here and why is it static?

The idea is that the table view doesn't necessarily destroy a cell after it has been used but can recycle it to improve performance. The identifier is used to get the correct cached cell back and not that of another table view. You can even use several identifiers for the same table view if you, for example, have two different cell types that you need all the time.
Imagine to have a cell that consists of a label and an image view. When the cell is initialized, the cell view needs to be created, the label needs to be created and the image view needs to be created. Now every cell of that type looks the same, they only differ in content. So instead of doing this setup all over again every time, an unused cell is sometimes returned where you only fill in the new content.

Static variable will be constructed only once and it avoids memory creation all the time when ever cellforrowatindexpath method is called.it's lifetime will exist through out that file as it is local variable whereas for global static its life time will be existed through out the application

CellIdentifier is a way to tag tableViewCells. This is so that uitableView can know which uitableviewcell to pull out when it needs to reuse it.

The cell recycling was already explained in a previous question. Static variables are “global” for the particular .m file, they are shared between all the instances of the class. That makes sense, as this string is just an identifier – having a separate instance of the same string for each instance of the class would be a waste.

You use a cell identifier like a tag to identify the cell when you try and reuse one by dequeueing it.
It's static because this way you only define it once but it is shared across all objects of the class within this method.

Related

Table View Returning Funny Error

Okay I am creating a Table View using objective c, but the data source is not working correctly...
My error:
2012-06-02 20:14:39.891 Dot Golf Scoring[195:707] *** Assertion failure in -[UITableView _createPreparedCellForGlobalRow:withIndexPath:], /SourceCache/UIKit/UIKit-1914.85/UITableView.m:6061
2012-06-02 20:14:39.895 Dot Golf Scoring[195:707] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
My Code:
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 16;
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return #"Comments On Your Round";
}
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
cell.textLabel.text = #"Text Label";
return cell;
}
Why is the table view not getting filled with this fake data???
You are never initializing cell. Use this code:
- (UITableViewCell *)tableView:(UITableView *)tableView2 cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:#"UITableViewCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"UITableViewCell"]
autorelease];
cell.textLabel.text = nil;
}
if (cell) {
//customization
cell.textLabel.text = #"Text Label";
}
return cell;
}
You say you are a noob.... let me explain
First of try picking up the book:
The Big Nerd Ranch Guide
What you are thinking is that dequeueing is basically initializing right? NO! Dequeuing is basically nilling any cell that is not visible, aka you scroll past it. Therefore, cell == nil will be called in probably four situations (that I can think of):
When we first setup the table view (cells will be nil)
When we reload data
Whenever we may arrive at this class
When the cells becomes invisible from the table view
So, the identifier for dequeuing is like an ID. Then in the statement to see if cell is nil, we initialize cell, you can see the overridden init method: initWithStyle. This is just what type of cell there is, there are different types with different variables you can customize. I showed you the default. Then we use the reuseIdentifier which was the dequeuing identifier we said earlier. THEY MUST MATCH! I nil textLabel just for better structure, in this case each cell has the same text so it won't matter really. It makes it so the cell that dequeues comes back with the right customization you implemented. Then once cell is actually valid, we can customize.
Also, you are using the same text for each cell. If you do want to have different text for each cell, familiar yourself with NSArray. Then you could provide the array count in numberOfRowsForSection and then do something like this:
cell.textLabel.text = [array objectAtIndex: [indexPath row]];
Where indexPath is the NSIndexPath argument provided in the cellForRowAtIndexPath method. The row variable is the row number, so everything fits!
Wow, that was a lot to take in right!
Now go stop being an objective-c noob and start reading some books!
For more info read:
Table View Apple Documentation
I don't think you read the Table View Programming Guide or understood the reuse mechanism of UITableViews ;)
Cells in UITableViews are reused/recycled, to avoid reallocating an instance of the UITableViewCell class each time you need a cell. This is because UITableView needs a lot of reactivity, especially when scrolling the tableview as the scrolling needs to be fast, and allocating a new UITableViewCell instance each time would make the tableview hang for a second while the instance is created.
So the idea behind UITableViewCell reuse mechanism is to allocate the minimum amount of cells, and each time you need a cell, try to reuse/recycle a cell that was previously allocated but is no longer user (because it is offscreen since you scrolled).
But if there is no cell available to reuse, you need to allocate one yourself!.
You forgot to do this part in your code, that's why you end up returning a nil cell, which throws the exception.
So the typical code to do this is :
static NSString* kCellId = #"Cell";
// First, try to reuse a cell that was previously allocated
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:kCellId];
// here, if a cell is returned, that means that we have an old cell
// that was used before but is no longer onscreen (so we can recycle it
// and just actualize its content)
// but if cell is nil, this means the UITableView didn't have a cell available to reuse
// so we need to create a new one
if (cell == nil)
{
// So if we didn't have a old cell ready to reuse that have been returned, create one
cell = [[[UITableViewCell alloc] initWithReusableIdentifier:kCellId] autorelease];
// And configure every properties of the cell that will be common to every cell
// and won't change even if the cell is recycled, eg:
cell.textLabel.textColor = [UIColor redColor];
cell.textLabel.font = [UIFont boldSystemFontOfSize:12];
// etc
}
// And at this point, we have a cell, either newly created or that have been recycled
// So we configure every property that is row-dependant and change for each row, eg:
cell.textLabel.text = [myTextsArray objectAtIndex:indexPath.row];
NB: I never used storyboard but AFAIK, when you use storyboard, you don't need to have the "if" statement and create the cell when no reusable cell is avaiable, as storyboard will create it for you using the cell design in your storyboard. But this is the only case when you don't need to allocate the cell youself.

issue when trying to create my own cell

I'm working on customizing my cell like below
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"reusedCell";
DetailCell *cell = (DetailCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Customize the cell of each row from table
if ( cell == nil ) {
NSLog(#" MY CELL IS NIL");
cell = [[DetailCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
else
NSLog(#" MY CELL IS NOT NIL");
}
By debugging, I know that line 4-5 are not reached at all
MY CELL IS NOT NIL
Therefore, I cant create my own cell. According to apple documentation
Typically, the first thing the data source should do is send dequeueReusableCellWithIdentifier: to the table view, passing in a reuse-identifier string. If the table view does not return a reusable cell object, the data source creates one, assigning the object a reuse identifier in the final parameter of initWithStyle:reuseIdentifier:
Therefore, in my situation, the table view does return a reusable cell object... Where it comes from then...
Have any one experienced this issue before. Please help thanks.
The first time a list is shown, cell will always be nil. Then as cells are scrolled off the ends, they are recycled through the reusable list. So sometimes when you debug, the cell is nil, and sometimes you get a recycled cell. Also, if you use the same identifier (i.e. #"reusedCell") on multiple lists (which you shouldn't), a cell from another list may get recycled into your list. That's why you should keep the cell identifiers unique.
If you want to create new cells each time when the user scrolls the table means, you need to override the reuseIdentifier property of the UITableViewCell in your custom cell class.
- (NSString *) reuseIdentifier
{
return #"";
}
This will let you to create new cell. But, check out the memory usage. Bcoz you are creating new cell each time when the user scrolls the table.

Deleting a cell in tableview, the cell also can be reused,why?

at first,i delete one cell in tableView,then i want to add another new cell in the tableView,but the new cell is not new,it is just the cell which i delete. i know what dequeueReusableCellWithIdentifier means.i think when delete one cell,the cache in dequeueReusableCellWithIdentifier is also deleted.why not?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"table reload");
PDFModel* tempmodel=[PDFArray objectAtIndex:indexPath.row];
NSString * CellIdentifier=tempmodel.PDFName;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSLog(#"cell %x",cell);
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
//custom the cell
cell.text=...
}
return cell;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle==UITableViewCellEditingStyleDelete)
{
[PDFArray removeObjectAtIndex:indexPath.row];
NSMutableArray* array=[[NSMutableArray alloc] init];
[array addObject:indexPath];
[self.tableView deleteRowsAtIndexPaths:array withRowAnimation:UITableViewRowAnimationRight];
}
}
There's no reason to delete the UITableViewCell since it can be reused. That cell won't be displayed until it is processed again by cellForRowAtIndexPath, so it won't show up with the old data. The laid-out cell, though -- the view and all of its subviews -- can be reused after new data replaces the old data.
Think of it like each cell is a whiteboard (dry erase board) where it's cut down to a certain size and all of the basic information areas are etched into the surface: a place for some text, another area for a number, a special space for drawing a picture, etc. In tableView:cellForRowAtIndexPath: when you check whether the cell == nil, you're trying to find out if there are any already-etched whiteboards available that you can erase and write the new information on with your dry erase marker. If there aren't then it cuts out and etches a brand new board to write information on, but if there is one that can be erased then it just wipes off the old information with an eraser and writes in the new information. As you can imagine, this saves a lot of time.
When a user is scrolling the table what's actually happening is that when one cell (whiteboard) scrolls off the top and is no longer visible, the system erases the data and moves it to the bottom of the stack; it will be the next one to scroll into view. As a result the system only has to create as many whiteboards as can be visible at any one time -- the ones off-screen don't actually exist.
When you delete a cell all you're really doing is telling the table to (a) stop displaying that cell, and (b) setting a flag so the table can use the eraser to wipe off the old data but still reuse the whiteboard.
Creating cells can be very costly in terms of computing power. If a UITableView had to create a new cell every time one came into view while the user was scrolling -- in the analogy, if it had to cut out a new whiteboard and etch all of the areas into it every time one came into view -- the scrolling would be very jerky and look terrible. By reusing cells, replacing just the changing contents, the table can move smoothly as the user interacts with it.
Your question is a bit weird, but I guess you're wondering what the following code does?
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
What the code does, is that it tries to retrieve a cached UITableView cell. This is to reduce memory usage on the iPhone.
Once you delete a cell, you should also remove the data from the datasource (usually an array), e.g.:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle==UITableViewCellEditingStyleDelete)
{
// remove object from array ...
[dataArray removeObjectAtIndex:indexPath.row];
}
}
(Remember: you can only remove items from an NSMutableArray at runtime, not from a NSArray)
If the cell gets redrawn, you will first check which cell needs to be redrawn, then you retrieve the appropriate data from the datasource, this is all done by the UITableViewDataSource delegate methods. You only have to make sure to retrieve the right content from the array and update the cell's content on redraw, e.g. like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
// no cached cell is found, create a new cell ...
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// add some defaults for cells in the initializer,
// stuff that's done here should be important for
// all displayed cells, for example background image.
}
// update the cell's content, e.g.: - it will only display content that's currently in the array, deleted objects shouldn't exist in the array at this point
NSString *title = [dataArray objectAtIndex:indexPath.row];
[cell.textLabel setText:title];
return cell;
}
I think You can use
NSString *CellIdentifier = [NSString stringWithFormat:#"Cell%d",indexPath.row];
instead of
NSString * CellIdentifier=tempmodel.PDFName;
please replace it & check it. I think it's helpful to you.
You seem confused by the difference between deleting a cell (an objective-c UI object) and the data it displays.
When you allow a user to delete a cell in the user interface you need to delete the corresponding data from your data model
The implementation of UITableView is highly optimized in iOS and it caches cells (UI objects) to improve performance. You don't really need to be concerned about this as your table will be optimally loaded and displayed based on your data.
In tableView:cellForRowAtIndexPath: you do the following:
Attempt to dequeue a cell instance. This takes advantage of caching done by UITableView
If dequeue does not return a cell, create a new one.
Once you have a cell, configure it with the appropriate data from your data model
When a user chooses a delete action from a cell, delete the data from your data model. optionally, you can animate removing the cell from the table or simply reload the whole table.
The specifics of #4 depend on the user interface you want and how you've configured your data model.
The Apple docs are pretty good about explaining this.
http://developer.apple.com/library/ios/#documentation/userexperience/conceptual/TableView_iPhone/AboutTableViewsiPhone/AboutTableViewsiPhone.html

How to make a UITableViewCell with different subviews reusable?

I have a UITableView in which I display, naturally, UITableViewCells which are all of the same class, let's call it MyCell. So I have one MyCell.xib, one MyCell.h and one MyCell.m.
Unfortunately, this cells do contain one subview, which holds varying content, e.g. a train subview and a car subview. So if the UITableView is in need of a new cell, it's always a MyCell but sometimes it contains a train subview and sometimes a car subview.
Now, here is my problem: How to make MyCell properly reusable? The cell itself is reusable as intended (In the .xib I defined it's identifier) but it's subview has to be created again and again for every cell. My first idea was to change the identifier of MyCell depending on it's content but unfortunately, reuseIdentifier can't be changed on runtime.
I could, however, implement my own - (NSString *) reuseIdentifier {} which I guess would work, though I wouldn't consider it great style. Is there a better way to do this?
Many thanks in advance!
EDIT: I realize I need to add that the subviews are stored in their own classes/xibs to keep their code seperated.
Instead of adding subviews to cells I'd suggest that you create for every kind of cell your own class. If you have the kinds: train, car, bike, boat and airplane I would create five subclasses.
As I understand Apple the reuse mechanism with the identifier is just for that case: different types of cells get their own identifier, not every single cell a special one. Just to point how I interprete the whole thing.
In Apple's Table View Programming Guide for iOS / Characteristics of Cell Objects, the 3rd paragrpah delivers some insight into the meaning of the reuse identifier.
I've written myself a small TableViewCellFactory class which makes my life easier to create cells with the interface builder and have those in my app within minutes.
First of all a small example on how to use cellForRowAtIndexPath and the factory as well as setting content for a cell.
I create a fresh cell with the factory which needs the tableView so it can handle the reuse logic. Next thing is to let a method fill in the content for the cell. In this case it's a cell which shows a video clip with some text.
Data Source delegate method and helper
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)anIndexPath
{
VideoClipTableViewCell *cell = [TableViewCellFactory videoClipTableViewCellWithTableView:aTableView];
[self configureVideoClipCellWithCell:cell andIndexPath:anIndexPath];
// code to decide what kind of cell not shown, but it could be here, just move the model
// access code from the configure cell up here and decide on what you get
return cell;
}
Next comes the data source helper to put content into the cell. Get the content from my model array and set the properties. Note, this does everything by reference, nothing is returned.
- (void)configureVideoClipCellWithCell:(VideoClipTableViewCell *)aCell andIndexPath:(NSIndexPath *)anIndexPath
{
VideoClip *videoClip = [videoClips objectAtIndex:anIndexPath.row];
aCell.videoTitleLabel.text = videoClip.title;
aCell.dateLabel.text = videoClip.date;
// more data setting ...
}
TableViewFactory
This class consists mainly of convenience methods and some boilerplate methods to do the real work.
// Convenience static method to create a VideoClipTableViewCell
+ (VideoClipTableViewCell *)videoClipTableViewCellWithTableView:(UITableView *)aTableView
{
return [self loadCellWithName:#"VideoClipTableViewCell" tableView:aTableView];
}
// method to simplify cell loading
+ (id)loadCellWithName:(NSString *)aName tableView:(UITableView *)aTableView
{
return [self loadCellWithName:aName
className:aName
identifier:aName
tableView:aTableView];
}
// method with actually tries to create the cell
+ (id)loadCellWithName:(NSString *)aName
className:(NSString *)aClassName
identifier:(NSString *)anIdentifier
tableView:(UITableView *)aTableView
{
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:anIdentifier];
if (cell == nil) {
UINib * nib = [UINib nibWithNibName:aName bundle:nil];
NSArray * nibContent = nil;
nibContent = [nib instantiateWithOwner:nil options:nil];
for (id item in nibContent) {
if ([item isKindOfClass:NSClassFromString(aClassName)]) {
cell = item;
}
}
}
return cell;
}
I've thrown out the whole error and exception handling just to keep the example short. If someone is interested I'd add the code.
Some important things about the usage is:
The connected class name, the reuse
identifier and the nib name are all
the same so a cell can be created
with only one string constant, else
the long loadCellWithName has to be
used.
Don't forget to set the reuse identifier in interface builder.
The nib should contain only one TableViewCell (can be changed with some coding though)
Don't set outlets of the File's Owner, use those of the tableViewCell
Set the class identity of the cell to a corresponding class which should be created foremost
Look at the screenshot
Thoughts on subclassing own custom cells
It's actually easy to subclass your own cell, add a few properties to it, make them available in IB with outlets, choose the new extended class in IB for your nib file.
The main problem is interface itself. It's not easily done to have different kinds of cells based on a custom cell in interface builder. The first approach would be to copy the nib file, rename it and use it with all the existing references and link the new ones to differing outlets. But what happens if the base cell has to be changed? Going through all kinds of inheriting cells could be a tedious task.
I just stumbled across Custom views in Interface Builder using IBPlugins on Cocoa with Love. It's a nice tutorial how to extend the components Library in IB. Our custom base cell could become an item in the library and become the template we've been looking for. I think this approach is the right way to choose. Still, looking at necessary steps, it's not just done within 5 minutes.
Interface builder is a helpful tool, allowing us to rapidly create views, but when it comes to reusability through subclassing, there are big steps necessary to create maintainable views. A pity.
Creating the views with code only I think one is better off with subclassing if it comes to more than one level of inheritance or many ancestor classes for just one base view.
EDIT
On the other hand, Apple warns about excessive use of subviews in a cell:
However, if the content of a cell is
composed of more than three or four
subviews, scrolling performance might
suffer. In this case (and especially
if the cell is not editable), consider
drawing directly in one subview of the
cell’s content view. The gist of this
guideline is that, when implementing
custom table-view cells, be aware that
there is a tradeoff between optimal
scrolling performance and optimal
editing or reordering performance.
Right now any approach has its drawbacks and advantages:
Too man subviews will hit
performance, easily done with IB
Drawing with code will result in a
hard to maintain code base but will
perform better
Skipping IB makes
subclasssing of template cell classes
easier
Hierarchy through subclassing
difficult to achieve with IB with nib
files
There are a couple of different ways to do this. You need a way to access that subview and reset or change it on reuse.
You could subclass UITableViewCell with your own class of cell that has a property for the train or car view. That way you could access and change that view when the cell is reused.
Assign a different identifier to each type of cell:
`
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CarCellIdentifier = #"CarCell";
static NSString *TrainCellIdentifier = #"TrainCell";
if(indexPath == carCellNeeded) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CarCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CarCellIdentifier] autorelease];
[cell addSubview:carView];
}
} else if(indexPath == trainCellNeeded){
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TrainCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TrainCellIdentifier] autorelease];
[cell addSubview:trainView];
}
}
return cell;
}
Or assign a special tag to that sub view you are adding and when the cell comes back around again to be reused you can access that specific subview by its tag.
I would add both custom subviews to the nib and connect them to an outlet. And depending on the content I would hide one of them when you configure the content of your cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = /* load from nib */
}
if (/*indexPath conditionForTrainCell*/) {
cell.trainSubview.hidden = NO;
cell.carSubview.hidden = YES;
// configure train cell
}
else {
cell.trainSubview.hidden = YES;
cell.carSubview.hidden = NO;
// configure car cell
}
return cell;
}
the simplest is to make a custom UITableViewCell subclass and create a xib for it. Set the root view in the xib as an uitableviewCell and set its class to your UITableViewCell subclass. Set the file owner class as your TableViewController subclass and add all the subviews you want to it. Then you can simply:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * CellIdentifier = #"cellIdentifier";
TableViewMessageCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([TableViewMessageCell class])
owner:self
options:nil] lastObject];
}
Message * message = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.message = message;
return cell;
}

UITableView dequeueReusableCellWithIdentifier Theory

When apple developed the UITableView for the first iPhone they had a problem in performance when scrolling through it. Then one clever engineer discovered that the cause of this was that allocation of objects comes with a price, so he came up with a way to reuse cells.
"Object allocation has a performance cost, especially if the allocation has to happen repeatedly over a short period—say, when the
user scrolls a table view. If you reuse cells instead of allocating
new ones, you greatly enhance table-view performance."
Source: iOS Reference Library
To reuse a cell you use:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Now, what I am wondering is, what actually happens here? Does it look in the TableView if there is a cell with that identifier and just returns that one? Well yea duh, but if it sends a reference instead of allocating and I have a table view with let's say 4 cells with the same identifier all visible. How can it multiply itself into four instances without allocating?
I want to know this because I am building a calendar type component and all the cells have the same structure only the text within changes. So if I could somehow reuse my cells instead of allocating I think I might get a better performance.
My own theory is that it allocates the four cells (simply because it has too). When a cell disappears from the screen it will be put in the TableView reuse queue. When a new cell is needed it looks in the que if a cell with the same identifier is available, it invokes prepareForReuse method on that cell and it removes itself from the queue.
dequeueReusableCellWithIdentifier: only returns a cell if it has been marked as ready for reuse. This is why in almost every cellForRowAtIndexPath: method you will see something like
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (nil == cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
// Do something to cell
return cell;
In effect, enough rows will be allocated to fill the visible part of the tableview (plus one or two more). As cells scroll off screen, they are removed from the table and marked as ready for reuse. As the queue of "available cells" grows, your line that asks for a dequeued cell will start obtaining a cell to use, at which point you will not have to allocate anymore.
The code for deqeueueReusableCellsWithIdentifier: will look something like this:
(Taken from one of my own projects where I do something similar with views/pages in a paged scroll view)
- (UIView*) dequeueReusablePage
{
UIView* page = [reusablePages_ anyObject];
if (page != nil) {
[[page retain] autorelease];
[reusablePages_ removeObject: page];
}
return page;
}
So it keeps a simple NSMutableSet with reusable objects.
When cells scroll off the screen and are not longer visible, they are put in this set.
So you start with an empty set and the set will only grow if you actually have more data to show then is visible on the screen.
Used cell scrolls off the top of the screen, is put in the set, then taken for the cell that appears at the bottom of the screen.
The purpose of dequeueReusableCellWithIdentifier is to use less memory. if we use 100 cells in a tableView then need to create 100 cells every time.It reduce the app functionality and may cause crash.
For that dequeueReusableCellWithIdentifier initialise the particular number of cells that we created and the cells will use again for further processing.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *TableIdentifier = #"YourCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TableIdentifier];
}
ExternalClassTableViewCell *myCell = [[ExternalClassTableViewCell alloc]init];
myCell.MyCellText.text = [tableData objectAtIndex:indexPath.row];
myCell.MyCellImage.backgroundColor = [UIColor blueColor];
return cell;
}