UICachedDeviceWhiteColor and NSAutoRelease for table cell mem leaks - objective-c

I have a memory leak that displays UICachedDeviceWhiteColor. I'm not using UICachedDeviceWhiteColor anywhere and a search on it turns up people saying this is a bug in the iPhone-SDK. I found this blog entry:
http://piezoelectrics.blogspot.com/2009/02/uicacheddevicewhitecolor-leak-in-iphone.html
but I can't find
#import "NSAutoreleasePool.h"
I get an "error: NSAutoReleasePool.h: no such file or directory". Is there a fix for this memory leak or a correct way to allocate table cells from nibs?
Here's how I'm currently doing it:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:#"CellNameIdentifier"];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"CellName" owner:self options:nil];
//cellName is IBOutlet to XIB's tablecell. I reference it several times in this calss
cell = cellName;
}
return cell;
}
I don't see an alloc here so why would there be a mem leak? Could this be a problem:
#property (nonatomic, retain) IBOutlet UITableViewCell *cellName;

Because of your property declaration, the sythesized setter for your cellName property will retain the object passed to it.
You should send a release message to cellName in your dealloc method.
Furthermore, there is no need to load the nib every time the cellView is requested. Either check if cellName != nil and return it or set the reuseIdentifier on the cellView so that it can be found by dequeueReusableCellWithIdentifier.

Actually, if you're using a NIB for your table view cell (not normally necessary unless you're doing something really custom) you will have to load it each time you didn't get a hit on the reusable table view cell. I think the following code looks a bit cleaner:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyID"];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CellName"
owner:self options:nil];
cell = [nib objectAtIndex:1];
}
The objectAtIndex:1 trick works if the cell is the first object in the NIB (the zero object is the file owner).
Some notes for doing table view cells:
Don't retain your cell objects either implicitly by assigning to a property or manually. This will make the the reusable table cell functionality not work properly since it can't free the cell memory.
Don't forget to set the cell reuse identifier in interface builder since you can't do it in code if you're using a NIB.
Always make sure the cell is autoreleased. Either do it manually, or ensure you're using a cocoa function that returns autoreleased memory (as the objectAtIndex: method does).

Related

Unrecognized selector sent to instance Custom Cell

I am trying to make a Custom Cell for my UITableView.
So, I create a class (UITableViewCell) and I named HomeCell. This class has a xib file with label named titleCell.
In my cellForRowAtIndexPath method I wrote:
HomeCell *cell = (HomeCell *)[tableView dequeueReusableCellWithIdentifier:#"HomeCell"];
if(cell == nil){
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:#"HomeCell" owner:self options:nil];
for(id oneObject in xib){
if([oneObject isKindOfClass:[HomeCell class]]){
cell = (HomeCell *) oneObject;
}
}
}
//Get object at index data
HomeObjectCell *tempObject = [self.dataForCell objectAtIndex:indexPath.row];
NSLog(#"Title : %#",tempObject.title); // The tempObject.title return a NSString example
cell.titleCell.text = tempObject.title; // <-- ERROR !
When I run the application, I got:
[UITableViewCell titleCell]: unrecognized selector sent to instance 0x6e61890
Any idea?
Are you absolutely sure that:
1) .. your class "HomeCell" is named correct for the table cell? Select the cell, select 'identity inspector' -> section 'Custom Class' and take a look
2) .. referencing outlets with connections to each label/ui image is made? Try select your cell again, select 'Connections inspector' and take a look
In the description you mentioned that HomeCell has member named titleLable.
In the code you have used cell.titleCell.text.
So, I think you should use cell.titleLabel.text instead of cell.titleCell. The crash message is also suggesting same thing that there is no proprty with cell.titleCell exists in HomeCell.
Check to see if the file owner in the xib file is of Class HomeCell.

Can't get contents of file from a .plist file into an array

I'm trying to create a create a simple app where I have a TableView that should display the contents of a file. I have created a Table View in IB and dragged it's delegate and data source to the file's owner and I have manually created a .plist file with 1 array that have 2 items.
In my TableViewController.h i have declared an array.
NSArray * posts;
In my implementation file I have declared the required methods for UITableViewDataSource like this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"Returning num sections");
return posts.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// create a cell
UITableViewCell * post = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"post"];
// fill it with content
post.textLabel.text = [posts objectAtIndex:indexPath.row];
// return it
return post;
}
And in my ViewController 'viewDidLoad' method I try to add the content of my file to the 'posts' array like this:
- (void)viewDidLoad
{
NSString * postFile = [[NSBundle mainBundle] pathForResource:#"Posts" ofType:#"plist"];
posts = [[NSArray alloc] initWithContentsOfFile:postFile];
NSLog(#"%#", postFile);
NSLog(#"%i", posts.count);
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
NSLog(#"%i", posts.count); returns 0, despite that I have added values to my .plist file. And nothing is displayed in the table view.
Suggestions on how so solve this would be appreciated.
I think you need to reload your table after you've loaded your postFile NSArray. If your view controller is a UITableViewController, try adding the following line of code to the end of your viewDidLoad method:
[self.tableView reloadData]
(On an unrelated note, you should also make your call to the super class the first thing you do in the viewDidLoad method, hence the comment the xcode template gives you.)
Edit: Problem with count.
I think you also have a problem with your debugging. count isn't a property of NSArray, so you can't use the dot syntax with it. You should be sending a message to your NSArray instance i.e. [posts count].
Ok, it seems like Xcode 4 creates plists with Dictionary as it's root type. If you want to use an array you have to open the .plist file in another text editor (probably doable in Xcode too) and change < dict >< /dict /> to < array >.
Also, it wasn't necessary to use an array at all. This also worked:
// Changed my array to a dictionary.
NSDictionary * posts;
// Get the cell text.
NSString * cellText = [[NSString alloc] initWithFormat:#"%i", indexPath.row];
// fill it with content
post.textLabel.text = [posts valueForKey:cellText];

warning received at objectsAtIndexes

I am trying to fetch data from core data, it works fine. But when I try to fill a Table Cell then it gives me warning at the following line
cell.textLabel.text = [fetchedObjects objectAtIndex:indexPath.row];
Warning : passing argument 1 of 'objectsAtIndexes:' makes pointer from integer without a cast.
fetchedObjects is an NSArray object declared in header file. Kindly guide me where I am doing wrong. I can provide whole method for more understanding.
Regards.
EDIT
Here is my complete method code:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"] autorelease];
cell.textLabel.text = [fetchedObjects objectAtIndex:indexPath.row];
return cell;
}
Your posted code says you're calling objectAtIndex, which does indeed take an integer parameter. However, your error says you're using objectAtIndexes, which takes a pointer to an NSIndexSet. As indexPath.row is not an NSIndexSet pointer, you're getting that warning (saying that you're trying to use a raw integer as a pointer).
Check your actual code. Almost certainly, you're using objectAtIndexes by mistake.

Trouble subclassing UITableViewCell (2 warnings)

Following the documentation on custom cells from a NIB (the Dynamic option), I have this method. (The view itself is not a UITableViewController, but it's hooked up properly.)
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"ReusableCell";
UITableViewCell *cell = (LoadGameCell *)
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[NSBundle mainBundle]
loadNibNamed:#"LoadGameCell" owner:self options:nil];
cell = loadGameCell;
self.loadGameCell = nil;
}
/*
cell setup
*/
return cell;
}
The first line in the if statement is the bit I'm having trouble with.
Incompatible pointer types assigning to 'UITableViewCell *' from 'NSArray *'
Incompatible Objective-C types assigning 'struct NSArray *',
expected 'struct UITableViewCell *'
No errors/crashes running the app with these warnings, but I'd rather not ignore/suppress them. It'll hurt a whole lot more later on.
If it isn't a direct result of the warnings above, there's another problem. I can't get the method to take views, only labels. (That is, I can customize a label, but not the image view it's sitting next to.)
As the documentation will tell you, loadNibNamed:owner:options: returns an array. To access the cell in the NIB (assuming it is the only root-level object in the NIB), call objectAtIndex:0 on the result of loadNibNamed:owner:options.

Multiple UiTableViewCell subclasses in a single UiTableView? Why is this code not working

Okay so Here's my issue. I have a table, a bit like Apple's addressBook.app that uses different cell subclasses to display information in different ways. On this particular app I have 3 different UiTableViewCell subclasses, all with their own nibs.
I can't get the code right :-( if I try and access the instance variable of my cell in the CellForRowPath method it says' (ERROR request for member 'type' in something not a structure or union') Now I assume that the word 'something' is the root of my problem.. The code I am about to paste obviously doesn't setup my cell properly. Here is the code:
There are 3 if statements to get the right cell subclass depending on the section being called:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *PoolFacilityCellIdentifier = #"PoolFacilityCellIdentifier";
static NSString *PoolFacilityAddressCellIdentifier = #"PoolFacilityAddressCellIdentifier";
static NSString *PoolFacilityPoolCellIdentifier = #"PoolFacilityPoolCellIdentifier";
NSUInteger section = indexPath.section;
//Creat a generic container for cell which will be cast during one of the next two statements
id cell;
//Change the identifier depending on cell type. MIGHT NEED TO ADD SOMETHING ELSE FOR POOLS AND PRICES
if (section == KAddressIndex) {
cell = (PoolFacilityAddressCell *)[tableView dequeueReusableCellWithIdentifier:PoolFacilityAddressCellIdentifier];
if (cell == nil) {
//Load the appropriate nib for the type of cell being asked for.
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"PoolFacilityAddressCell" owner:self options:nil];
for (id object in nib) {
if ([object isMemberOfClass:[PoolFacilityAddressCell class]])
cell = (PoolFacilityAddressCell *) object;
}
}
}
else if (section == KPoolIndex) {
cell = (PoolFacilityPoolCell *)[tableView dequeueReusableCellWithIdentifier:PoolFacilityPoolCellIdentifier];
if (cell == nil) {
//Load the appropriate nib for the type of cell being asked for.
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"PoolFacilityPoolCell" owner:self options:nil];
for (id object in nib) {
if ([object isMemberOfClass:[PoolFacilityPoolCell class]])
cell = (PoolFacilityPoolCell *) object;
}
}
}
else {
cell = (PoolFacilityCell *)[tableView dequeueReusableCellWithIdentifier:PoolFacilityCellIdentifier];
if (cell == nil) {
//Load the appropriate nib for the type of cell being asked for.
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"PoolFacilityCell" owner:self options:nil];
for (id object in nib) {
if ([object isMemberOfClass:[PoolFacilityCell class]])
cell = (PoolFacilityCell *) object;
}
}
}
Any help would be greatly appreciated!!! I import all the header files for my cell subclasses.
The code that throws my error is this (specifically the cell.type.text = thePool.type) cell.type is an Iboutlet UiLabel:
if (section == KPoolIndex) {
NSMutableArray *thePools = [allSections objectForKey:sectionAsNumber];
if ([thePools count] > 0) {
[cell setPromptMode:NO];
//Set the label and Pool from the Pool Object
Pool *thePool = [thePools objectAtIndex:row];
cell.type.text = thePool.type;
}
}
Thanks,
Dan
Matt Gallagher has an article that lays out a cleaner way to accomplish this type of table.
He breaks out the cell specific code into their own classes. Makes it alot easier to manage.
cocoawithlove.com - Heterogeneous cells in a UITableViewController
In your code, this line looks like trouble
cell.type.text = thePool.type;
thePool is a "Pool" object, does it have a property named "type" ?
Check that the #property and #synthesize are setup correctly for "type" in your "Pool" object.
I believe it may be due to the fact that your cell is declared as id and you're trying to access a property off one of your cell subclasses. You can send all the messages you want to an object of type id, but trying to access properties on an object of type id will fail at compile time. Also, be sure to import the .h files containing your subclasses.
I might have a few answers to my own question:
1 - As drew has mentioned the cell remains of type ID. I thought my following if functions would typeCast the cell into being a specific cell type but that doesn't seem to be the case.
2 - A solution would be to have 3 big if statements for each type of cell. Each if statement ending in a
return cell;
call.
3 - The solution I'm going to try now is to have only one cell subclass instead of 3 and have a method in my cell subclass called
-(void)setCellBehaviour:(int)definedNumber
That way I can hide layers/labels etc and customize it how I wish.
update
I've achieved the required results like this:
I only have one cell subclass which has a single NIB with many different UIlabels.
I call this in the cellForRowAtIndexPath: method of my uiviewcontroller, once the cell has been alloc/init.
//Set the behaviour
[cell setCellBehaviour:KCellBehaviourName];
This references the definitions I set in the header file of the view controller:
//Definitions for cell type behaviour. Passed to cell during creation.
(HASH)define KCellBehaviourStandard 0
(HASH)define KCellBehaviourName 1
(HASH)define KCellBehaviourAddress 2
(HASH)define KCellBehaviourPool 3
(Cant seem to do hashes in this post)
Then my UITableViewCellSubclass has this methos which we called earlier:
-(void)setCellBehaviour:(NSUInteger)definedBehaviour {
switch (definedBehaviour) {
case KCellBehaviourStandard:
self.label.hidden = NO;
self.value.hidden = NO;
self.length.hidden = YES;
self.poolType.hidden = YES;
break;
case KCellBehaviourName:
self.label.hidden = NO;
self.value.hidden = NO;
self.length.hidden = YES;
self.poolType.hidden = NO;
break;
case KCellBehaviourAddress:
self.label.hidden = NO;
self.value.hidden = NO;
self.length.hidden = YES;
self.poolType.hidden = YES;
break;
case KCellBehaviourPool:
self.label.hidden = NO;
self.value.hidden = NO;
self.length.hidden = NO;
self.poolType.hidden = YES;
default:
break;
}
}
Oh my goodness this is much simpler than all of the answers are making it appear. I'm not going to discuss your overall design, just focus on why your code is not working.
This is the line that is triggering the error:
cell.type.text = thePool.type;
You can fix it by replacing that with these lines:
PoolFacilityCell* poolCell = (PoolFacilityCell *)cell;
poolCell.type.text = thePool.type;
The dot-syntax for accessing properties only works if the compiler knows what class you are working with. It can't deal with cell.type because cell is declared as type id, so at compile time it doesn't know that type is supposed to be an Objective-C property. The error message calls it "something not a structure or union" because it thinks you are treating cell like a plain C struct, which it is not, hence the error.
Incidentally, the lines where you write:
cell = (PoolFacilityAddressCell *) object;
are meaningless. Since cell is declared as id (any generic object), there is no need for the cast. Based on an earlier comment, you seem to think that type casting like that affects the cell variable somehow. It does not. The line above compiles into instructions that simply copy the memory address of the instance from object to cell. Type casting is there to avoid warnings and errors at compile time.