Dealloc of custom cell loaded from NIB is not called - iphone

So I have a subclass UITableViewCell named MCProductCell, which is loaded from a NIB. The problem is that when the table is released, the dealloc method of my custom cell is not called even once.
Here is some sample code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"MCProductCellIdentifier";
MCProductCell *cell = (MCProductCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
// Boolean value needed to determine if it is a reused cell or not. If it's not reused we have
// to start the thread that loads the image. For reused cells, that thread is started at the
// end of the scrolling
BOOL recycled = YES;
if (cell == nil) {
NSLog(#"cell alloc");
recycled = NO;
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MCProductCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
MCProduct *product = [products objectAtIndex:indexPath.row];
cell.product = product;
cell.cartViewController = self;
cell.productImage = product.cachedThumbnailImage;
if (product.cachedThumbnailImage == nil) {
cell.productImage = [ViewControllerUtils getDefaultImage];
if (!recycled)
[NSThread detachNewThreadSelector:#selector(loadImage:) toTarget:cell withObject:cell.product.imageThumbnailUrl];
}
return cell;
}
And for some reason, when I first present my UIViewController, that contains the table, the dealloc method of my custom cell is called ONCE.
The problem is that in the dealloc method I want to remove the cell as an observer, and if it isn't called, then the cell isn't removed as an observer.
Also the tableview is an outlet.

I figured out, it must be because the retain count of the cell is not going down to 0.
Which means you have another retain.
My more experienced colleague thinks its because you are using the detachNewThreadSelector, which probably retains the cell.
He suggested you would load the image by using some type of asynchrony image such as
https://github.com/nicklockwood/AsyncImageView/
Good luck.

How is the 'cell.cartViewController' property defined? If it's retaining your controller object (self), then you probably have a retain cycle in there!

Related

Custom UITableViewCell Not Using .xib (Most Likely Because of Flaw in init Method)

I subclassed the UITableViewCell in order to customize it, but I think I'm missing something because: 1) It's not working and 2) There are a couple of things I'm confused on. Along with customizing the look of the .xib file, I also changed the backgroundView, and that part is working fine. The part that I least understand/am most confused about is the init method, so I posted that here. If it turns out that is correct, please tell me so I can post more code that may be the cause.
This is the init method, which I customized. I'm sort of confused around the "style" idea and I think I'm just returning a normal UITableViewCell with a different backgroundView. I mean, there's nothing in there that refers to the .xib or does anything but change the .backgroundView from the self:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier wait: (float) wait fadeOut: (float) fadeOut fadeIn: (float) fadeIn playFor: (float) playFor
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
CueLoadingView* lview = [[CueLoadingView alloc] initWithFrame:CGRectMake(0, 0, 320, 53)];
self.backgroundView = lview;
[self setWait:wait]; // in turn edits the lview through the backgrounView pointer
[self setFadeOut:fadeOut];
[self setFadeIn:fadeIn];
[self setPlayFor:playFor];
}
return self;
}
Other than the .xib and several setters and getters, this is the only real part of my code, that relates to retrieving a cell.
Additional Information:
1) This is my .xib, which is linked to the class.
2) This is the code that calls/creates the UITableView (the delegate/view controller):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"CueTableCell";
CueTableCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[CueTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier wait:5.0 fadeOut:1.0 fadeIn:1.0 playFor:10.0];
[cell updateBarAt:15];
}
return cell;
}
The easiest way (available since iOS 5.0) to create a custom table view cell in a nib file is to use registerNib:forCellReuseIdentifier: in the table view controller. The big advantage is that dequeueReusableCellWithIdentifier: then automatically instantiates a cell from the nib file if necessary. You don't need the if (cell == nil) ... part anymore.
In viewDidLoad of the table view controller you add
[self.tableView registerNib:[UINib nibWithNibName:#"CueTableCell" bundle:nil] forCellReuseIdentifier:#"CueTableCell"];
and in cellForRowAtIndexPath you just do
CueTableCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CueTableCell"];
// setup cell
return cell;
Cells loaded from a nib file are instantiated using initWithCoder, you can override that in your subclass if necessary. For modifications to the UI elements, you should override awakeFromNib (don't forget to call "super").
You have to load the cell from the .xib instead:
if ( cell == nil ) {
cell = [[NSBundle mainBundle] loadNibNamed:#"CellXIBName" owner:nil options:nil][0];
}
// set the cell's properties
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"CueTableCell";
CueTableCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
NSArray *array = [[NSBundle mainBundle] loadNibNamed:#"CueTableCell XibName" owner:self options:nil];
// Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
cell = [array objectAtIndex:0];
}
return cell;
}

Using multiple custom cells without reusing identifier

I feel like a real noob asking this, but here's my problem:
I want to show a tableView, with 7 custom cells. None of these cells is reused. That means the user will see 7 different cells, not more, not less.
I created the cells in the viewDidLoad method, and added all those cells in the listCells-array. After that, I used easy-mode to draw those cells:
UITableViewCell *cell = nil;
if (indexPath.row == 0)
{
static NSString *MyIdentifier = #"Cell";
cell = (DetAlertCell *)[localTableView dequeueReusableCellWithIdentifier:nil];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"DetAlertCell" owner:self options:nil];
cell = [listCells objectAtIndex:indexPath.row];
}
}
....
However, this won't work. It shows me a blank view. Every cell is created using a .xib-file and a .h and .m class. Is there anything that I'm missing and should do?
Just don't call the [localTableView dequeueReusableCellWithIdentifier:nil] and loa the correct cell for the index path.
Also you say that you load the cells in the viewDidLoad, then why do you load the nib:
[[NSBundle mainBundle] loadNibNamed:#"DetAlertCell" owner:self options:nil];
They should already be the array should they not.
And why to you check if the row is 0 then load the row, still will only load the first row.
Try this:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
return [listCells objectAtIndex:indexPath.row];
}

Custom UITableViewCell disappearing after scrolled past top

I have a custom UITableViewCell loaded from a nib file. Everything works fine until I scroll down past the last cell such that the table view has to bounce back when I let go.
Then, the cells towards the top of the list are blank and remain blank until I manually refresh the table view.
Image of issue: [iPhone Screenshot][1]
Here is my cell for row code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"TimeEntryCellIdentifier";
TimeEntryCell *cell = (TimeEntryCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"TimeEntryCell" owner:self options:nil];
for (id oneObject in nib) {
if ([oneObject isKindOfClass:[TimeEntryCell class]]) {
cell = (TimeEntryCell *) oneObject;
}
}
}
TimeEntry *aTimeEntry = [appDelegate.timeEntries objectAtIndex:indexPath.row];
cell.clientName.text = aTimeEntry.ClientName;
cell.category.text = aTimeEntry.CategoryName;
cell.hours.text = [NSString stringWithFormat:#"%#", aTimeEntry.Hours];
[TimeEntry release];
return cell;
}
Any ideas?
[1]: http://dl.nvthost.com.s3.amazonaws.com/Screen shot 2011-02-18 at 2.46.42 PM.png
[1]: http://dl.nvthost.com.s3.amazonaws.com/Screen shot 2011-02-18 at 2.46.42 PM.png
I don't think you should be doing the
[TimeEntry release];
there on the next to last line. You didn't allocate it in that method, just pulled a reference from the delegate. The retain count is probably zero at that point. When iOS starts releasing memory that TimeEntry will be dealloc'd.
The problem turned out the be the view hierarchy in IB. Even though I put a view in the cell and then dragged UILabels onto the view, they ended up on the same level as the view. After I moved the UILabels under the view it appears to be working.

iphone how to access a custom cell outside of cellForRowAtIndexPath

i have set up a tableview with custom cells. customCell is a class.
heres the code for a more accurate view:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSManagedObject *object = (NSManagedObject *)[entityArray objectAtIndex:indexPath.row];
NSString *cellIdentifier = [NSString stringWithFormat:#"asd%d", indexPath.row];
customCell *cell = [[tableView dequeueReusableCellWithIdentifier:cellIdentifier] autorelease];
//i tried setting a tag but dunno how to call it afterwards
[cell setTag:indexPath.row];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"customCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
}
if (cell.imagen != nil) {
[[cell imageView] setImage:[cell imagen]];
} else { /* setup image... */ }
}
-(void) webImageReady:(WebImage *)downloadedImage imageView:(UIImageView *)imageView cellTag:(NSInteger *)cTag
{
// This is the part where i want to access cell.imagen, this is actually wrong...
[[[imageView.superview viewWithTag:cTag] imagen] setImagen:downloadedImage.Image];
[imageView setImage:downloadedImage.Image];
}
Ok. now i want to access (reference) the cell.imagen property from a method outside cellForRowAtIndexPath, more precisely at a selector for a download finished (delegated)
Thanks in advance!
Do it inside cellForRowAtIndexPath if the image is downloaded, and on successful download of the image do [tableview setNeedsDisplay]
You shouldn't refer to the cell outside the cell creation method, you should consider the case the cell was rendered but while getting the image was scrolled out the dealloced or even reused for another cell, one way to solve it is to have image view array or something similar.
I think you should try using a third party lib that already doing it(among other things) called Three20. It have an object call TTImageView that gets a URL and loads it in the background, it solves all of the cases along with optimized caching

iPhone table view - problem with indexPath.row

I'm using indexPath.row do determine in which row of my tableview I do something. The title of my cells is containing a number which should be 1 in the first row and 18 in the last row, so I have 18 rows. This works for the first 11 rows, but after that, I have numbers in the title which seem to be generated randomly! Sometimes 16, then 5, then 18, then 12... and so on.
What's the problem with it/why does the indexPath.row variable behave like that?
My cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"myCell" owner:self options:nil];
cell = cell0;
self.cell0 = nil;
}
UILabel *label;
label = (UILabel *)[cell viewWithTag:1];
label.text = [NSString stringWithFormat:#"Cell %d", indexPath.row];
return cell;
}
Any more suggestions on how to solve the problem? I didn't get it working until now...
// Update with more code:
Here is how I declare the cell. It is in an XIB file (template "empty XIB") in which I just put the cell from the library in IB.
#interface myViewController : UITableViewController {
UITableViewCell *cell0;
}
#property (nonatomic, retain) IBOutlet UITableViewCell *cell0;
Then, at the top of the myViewController.m file:
#synthesize cell0;
My cellForRowAtIndexPath method is already posted above. It is equal to the cellForRowAtIndexPath method in the SDK documentation, and in Apple's example, it seems to work.
What are you trying to accomplish with cell0?
cell = cell0;
self.cell0 = nil;
It looks like you're creating a new cell, but somehow deciding to use an old one. The real culprit looks like the code that is loading the cell actually getting assigned anywhere.
Try just this instead:
if (cell == nil) {
cell = [[NSBundle mainBundle] loadNibNamed:#"myCell" owner:self options:nil];
}
Or perhaps:
if (cell == nil)
{
// TODO: try to avoid view controller
UIViewController *vc = [[UIViewController alloc] initWithNibName:#"IndividualContractWithResult" bundle:nil];
cell = (IndividualContractWithResult_Cell *) vc.view;
[vc release];
}
To would be easier to answer if you give the code where you create cells for your table view. It looks that there's a problem with reusing cells - you reuse previously created cells without setting a new value to it.
It sounds like you are not re-using cells but creating new ones when there are cells available. Look at the sample code for dequeueReusableCellWithIdentifier.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:#"MyCell"] autorelease];
}
cell.text = <your code here>;
return cell;
}
It would seem that you're incorrectly accessing a property here:
cell = cell0;
self.cell0 = nil;
Assuming that you have an instance variable named cell0, by setting it to nil, you may be releasing it before you're ready to use it.
The proper way to do this is:
cell = self.cell0;
self.cell0 = nil;
This way, if cell0 is declared as retain, you'll automatically get an autoreleased cell0 back, whereas if you reference cell0 directly (no self.), you'll get an unretained reference, which will disappear when self.cell0 = nil is called.
The advantage of using a nib-based cell here is that you can use outlets, rather than tags, to identify subviews. You've done the heavy lifting already, you might want to just add an outlet and subclass UITableViewCell to get access to the label.
You will need to retain and autorelease cell0, otherwise when you set self.cell0 = nil, then cell0 has no known references.
cell = [[cell0 retain] autorelease];
self.cell0 = nil;
You can also do this:
cell = self.cell0;
self.cell0 = nil;
.. Since any retain properties should implement their getters with the retain/autorelease pattern.