IPhone - Setting NSString from array, double standards! - iphone

In the following bit of code, I'm setting the table view cell text with a value from the NSMutableArray 'categories' which is a property of my view controller. That works fine.
But when I try the exact same code in another method, it crashes (it compiles without errors or warnings). If I change the following line in the didSelectRowAtIndexPath method:
NSString *categoryName = [categories objectAtIndex:indexPath.row];
to
NSString *categoryName = [[NSString alloc] initWithString:#"test"];
It works... any ideas?
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
NSString *categoryName = [categories objectAtIndex:indexPath.row];
cell.textLabel.text = categoryName;
return cell;
}
// Override to support row selection in the table view.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
printf("User selected row %d\n", [indexPath row] + 1);
ButtonsPageViewController *bView = [ButtonsPageViewController alloc];
NSLog(#"created instance of buttonspageviewcontroller");
NSString *categoryName = [categories objectAtIndex:indexPath.row];
NSLog(#"category name set");
bView.selectedCategory = categoryName;
NSLog(#"selected category property set");
[self.navigationController pushViewController:bView animated:YES];
NSLog(#"push view controller");
[bView release];
}

The difference between
NSString *categoryName = [categories objectAtIndex:indexPath.row];
and
NSString *categoryName = [[NSString alloc] initWithString:#"test"];
Is that the first line copies a pointer to the object (retain count does not change) whereas the second one creates a new object (retain count = 1).
In cellForRowAtIndexPath, when you set the text property, it copies or retains the string, so you're fine. In didSelectRowAtIndexPath you are setting a property of ButtonsPageViewController, which I assume is your own code, but perhaps it is not copying or retaining the object.
Also, the line
ButtonsPageViewController *bView = [ButtonsPageViewController alloc];
is going to lead to problems. You need to call init to properly initialize the object. All you've done in that line is allocate memory for it.
In general, it looks like you need to brush up on Retain/Release memory management. That should save you some trouble.

Like benzado says, it's an issue retaining the selectedCategory value in ButtonsPageViewController.
Are you using #property and #synthesize or are you writing your own accessors? If it's the former, you probably need to look at the property declaration attributes. Otherwise, it's probably a retain/release thing in your custom accessor.
The Declared Properties section of The Objective-C 2.0 Programming Laungauge is a good resource for rules of declaring synthesized accessors.

Related

iPhone:TableView cellForRowAtIndexPath method issue

I am facing a crashing problem in cellForRowAtIndexPath tableview delegate method
#interface EventListView : UIViewController <UITableViewDelegate, UITableViewDataSource>
IBOutlet UITableView *tblView;
NSMutableArray *arr_EventValues,*arr_Event_Details;
NSMutableArray *arr_EventListDetails;
#property(nonatomic, retain)NSMutableArray *arr_EventValues,*arr_EventListDetails, *arr_Event_Details;
#property(nonatomic, retain)UITableView *tblView;
- (void)viewDidLoad
{
appdelegate = (VibesGuideAppDelegate *)[[UIApplication sharedApplication] delegate];
ViewCalendar = [[CalendarView alloc] initWithNibName:#"CalendarView" bundle:[NSBundle mainBundle]];
[self.navigationController.navigationBar setHidden:YES];
self.arr_Event_Details = [[NSMutableArray alloc] init];
self.arr_EventValues = [[NSMutableArray alloc] init];
[super viewDidLoad];
}
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([self.arr_EventListDetails count] > 0)
{
return [self.arr_EventListDetails count];
}
return 0;
}
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
// ------------------------------- Custom cell ------------------------------
Customcell *cell = (Customcell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell = [[[Customcell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.text = #"Hello";
return cell;
}
* -[EventListView tableView:cellForRowAtIndexPath:]: message sent to deallocated instance 0x60e01b0 and I used tableview from xib and set all connections and also got arrayvalues in numberOfRowsInSection in method but in cellForRowAtIndexPath method is not called so please give me idea my issue....
Thanks in advance.
At first it seems your some variable gets released. Make sure you have properly allocated it. If you have declared #property for it you better use the variable as self.variable. You can use retain and in case of IOS 5 strong in property declaration.
Just to be sure you can track if any variable gets released via setting NSZombieEnabled to YES. With zombies enabled, messages to deallocated objects will no longer behave strangely or crash in difficult-to-understand ways, but will instead log a message and die in a predictable and debugger-breakpointable way. You can set NSZombieEnabled by the following steps.
Select Product from the menu bar above. Keep alt/option pressed and select "Test..." or "Run...". Go to the Arguments tab, and add NSZombieEnabled YES in the "Environment Variables" section.
Your tableview itself is already released - the error message says that you send the cellForRowAtIndexPath message to a deallocated instance of the table view - so your problem lies somewhere in not retaining or releasing the EventListView and cannot be seen in the code displayed here.
Check this one:
Table DataSource and delegate is set or not.
Used array in cellForRowAtIndexPath is properly set with property and synthesized as well used with self. name.
First of all you dequeue the cell and then create a new one. This is not a good practice, if you are able to dequeue a cell you should not create a new one. You should have something like this:
Customcell *cell = (Customcell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[[Customcell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
fix this and let me know if you are still running on the same problem.

Random Debugging / Wrong Memory Management

I have no idea what's wrong with my program...
I think something bad with the memory management, cos every time I try to execute the app i have a different result from the simulator.
When I run the app everything works fine. The date formatter works fine! I can see in the table all the cell formatted in the right way!
The interface is a tabController whit 2 tableView to show the content of a database and a tab with a view used to add element to the db.
If I go in the "Add Tab" i can add all the element I want, but when i switch back to the others tab the program crash with an "Exe_Bad_Access" (in the code below).
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"MovieTableCell" owner:self options:NULL];
cell = nibLoadedCell;
}
// Configure the cell.
UILabel *itemLabel = (UILabel *) [cell viewWithTag:1];
UILabel *priceLabel = (UILabel *) [cell viewWithTag:2];
UILabel *groupLabel = (UILabel *) [cell viewWithTag:3];
UILabel *dateLabel = (UILabel *) [cell viewWithTag:4];
NSDictionary *rowVals = (NSDictionary *) [shoppingListItems objectAtIndex:indexPath.row];
NSString *itemName = (NSString *) [rowVals objectForKey:#"item"];
itemLabel.text = itemName;
int groupid = [(NSNumber *) [rowVals objectForKey:#"groupid"] intValue];
groupLabel.text = Group[groupid];
NSNumber *price = (NSNumber *) [rowVals objectForKey:#"price"];
priceLabel.text = [priceFormatter stringFromNumber: price];
NSDate *dateValue = (NSDate *) [rowVals objectForKey:#"dateadded"];
NSString *str = [dateFormatter stringFromDate:dateValue]; //-->Here I got the Bad Access
[dateLabel setText:str];
return cell;
[itemLabel release];
[groupLabel release];
[priceLabel release];
[dateLabel release];
}
Here is the entire program, if someone want to have a look: http://cl.ly/A1yk
3 things:
1) Anything after your return statement will not run. The 4 lines after that will never get run.
return cell;
[itemLabel release];
[groupLabel release];
[priceLabel release];
[dateLabel release];
2) If those release statements did run, the next time you access those labels you will get a bad access error, because those UILabels will get deallocated. Don't call 'release' on any object you haven't called 'retain' on.
3) To understand if anything is wrong with dateFormatter, we'd have to see every piece of code that touches that variable.
Look at the memory management of dateFormatter. It's may be being over released. You can check by adding a
NSLog(#"Date formatter: %#", dateFormatter);
before the string call and see what shows up.
By they way, remove the [itemLabel release] etc. lines. (a) they are not being executed as they follow your return cell and (b) if they were called, they'd cause problems.
Your casting NSDate to the objectAtIndex, are you sure the object is not some other class is it? Also, is the dateFormatter variable initialised as it could be nil?
Try removing the release lines at the end. As you are not allocating the UILabel its better not to release those. Hope this helps!
I cant be sure if this might be the reason, coz exc_bad_access can occur for anything. But it seems like when you are reusing the same cell, you are never allocating it urself but instead getting it as nibloaded cell from the interface builder.
Try using the default code where u alloc/init a cell:
(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"MyCell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell =
[[[UITableViewCell alloc]
initWithFrame:CGRectZero
reuseIdentifier:cellIdentifier]
autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.text = label;
return cell;
}

NSArray released early

I'm having a problem with my iPhone app crashing when I scroll down on a UITableView. I set NSZombieEnabled to YES, and found out that the NSArray I'm using to fill the table is getting dealloced somehow.
#import "RootViewController.h"
#implementation RootViewController
#synthesize flashsets;
- (void)viewDidLoad
{
//Unrelated code removed from this post
NSString *listofsetsstring = [[NSString alloc]
initWithContentsOfFile:pathtosetsdata
encoding:NSUnicodeStringEncoding
error:&error];
flashsets = [listofsetsstring componentsSeparatedByString:#"\n"];
[super viewDidLoad];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [flashsets count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
NSLog(#"IndexPath.row = %i", indexPath.row);
cell.textLabel.text = [flashsets objectAtIndex:indexPath.row]; <<<< CRASH HERE!!
return cell;
}
#end
I'm getting message sent to deallocated instance 0x4ebae20 at the bolded line. In my .h I used #property (nonatomic, retain) NSArray *flashsets;, I thought the retain part should keep it from deallocating.
How do I keep it from doing this?
Problem is with :
flashsets = [listofsetsstring componentsSeparatedByString:#"\n"];
change it to
flashsets = [[listofsetsstring componentsSeparatedByString:#"\n"] retain];
edit: the retain in property is only used if you use the setter, so it will only work if you use the following line:
[self setFlashsets:[listofsetsstring componentsSeparatedByString:#"\n"]];
in you viewDidLoad it should be self.flashsets = this will insure the accessor method is used to set the value, and thus the 'retain' behaviour you specified on the property definition will be implemented.
flashsets = [listofsetsstring componentsSeparatedByString:#"\n"];//it returns autorealesed NsArray.So If you want Longer Use.you should get Owner ship from that array By Alloc or Copy Or Retain.
flashsets = [[listofsetsstring componentsSeparatedByString:#"\n"] retain];
or
flashsets = [[listofsetsstring componentsSeparatedByString:#"\n"]copy];
or
flashsets = [[NsArry alloc ] initWithArray:[listofsetsstring componentsSeparatedByString:#"\n"]];
I don't know if this will help, but you may want to use the setter and getter methods when referring to flashsets - the retain part of the property doesn't apply (I don't think) when setting the variable directly.

Memory Management in tableviewDataSource

Just a quick question really:
I'm running a method to pull records from an sqlite database into an array, then assigning the contents of that array to an instance variable.
#interface {
NSArray *items;
}
#implementation
// The population method.
-(void)populateInstanceVariable
{
NSMutableArray *itemsFromDatabase = [[NSMutableArray alloc] init];
// Sqlite code here, instantiating a model class, assigning values to the instance variables, and adding this to the itemsFromDatabase Array.
self.items = itemsFromDatabase;
[itemsFromDatabase release];
}
// viewDidLoad is calling the method above
-(void)viewDidLoad
{
[self populateInstanceVariable];
[super viewDidLoad];
}
// TableViewDataSource method - cellforIndexPath
- (UITableViewCell *)tableView:(UITableView *)passedInTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault];
// Load in my model from the instance variable - ***1
MyDataModel *model = [items objectAtIndexPath:indexPath.row];
// Assign the title to the cell from the model data
cell.textLabel.text = model.title;
// This is the part i'm stuck on, releasing here causes a crash!
[model release];
return cell;
}
#end
My question is two fold:
Is what i'm doing to assign data to the instance variable right? and am i managing the memory correctly?
How do i manage the memory for that model item in the tableview datasource? the only way i seem to be able to get it to run smoothly is if i don't release the *model object at all, but that causes leaks surely?
Cheers.
No, you're not managing memory correctly here:
you should use "reusable" UITableViewCells, most UITableView examples show how to do this, and
do not do [model release], you do not "own" the object in this case, you're just referring to it so you must not release it
Here's the typical cellForRowAtIndexPath:
-(UITableViewCell *) tableView:(UITableView *)atableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
// Dequeue or create a cell of the appropriate type.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
// settings that do not change with every row
cell.selectionStyle = UITableViewCellSelectionStyleGray;
}
// settings that change with every row
cell.textLabel.text = #"fill in your label here";
return cell;
}
Also, if you're using a DB for your data, you may want to look in to Core Data, Apple's data persistence/management framework, it includes the ability to hook aspects of your data entities directly up to UITableViews.
1) Populate method is correct. Don't forget to set the instance variable to nil in the dealloc. (I suppose you added a property/synthesize as you used the 'self.').
2) Do NOT release the model object. You did not retain, copy or allocated it in that method. By the other hand your initialization of the cell is wrong. Use the following: (Better for performance)
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *Identifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:Identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:Identifier] autorelease];
}
//Other code
}

IPhone SDK, Display an NSMutable array in Table view?

i have tryed to display my NSMutableArray in a Table View by following a tutorial. It has completley failed for some reason, i think i have a good idea why but cannot get around it, this is my code:
- (void) scoreSystem {
scoreArray = [[NSMutableArray alloc] init];
NSNumber *onescore = [NSNumber numberWithInteger:score];
[scoreArray addObject:onescore];
NSNumber *twoscore = [NSNumber numberWithInteger:score];
[scoreArray addObject:twoscore];
NSNumber *threescore = [NSNumber numberWithInteger:score];
[scoreArray addObject:threescore];
NSNumber *fourscore = [NSNumber numberWithInteger:score];
[scoreArray addObject:fourscore];
NSNumber *fivescore = [NSNumber numberWithInteger:score];
[scoreArray addObject:fivescore];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [scoreArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [scoreArray objectAtIndex:indexPath.row];
return cell;
}
I think it is because it wont let me link up everything properly in IB, it lets me put the data source and delegate to the Files owner, but then when i drag from the files owner to my view it says 'delegate' instead of 'view', i think its because i am doing it in the 'main window' not VC.
Is there any way round this?
Thanks!
harry.
You want to set the class that your code is in as the tableview's datasource. Create an instance of your class in IB (use the NSObject, and rename its class to YourClass).
This will create an instance of your class that will be available when the nib is decoded.
Then, control-drag from the tableview to your class, and set the datasource.
That's it! You should be able to set breakpoints in your -numberOfRowsInSection: method above, and see it called as soon as the table view comes in view. If you don't, check your connections and check for typos: the runtime is case-sensitive.
Well for some reason someone bumped this old thread. I might as well chime in. The reason this code has problems is because it is trying to set the text property of the cell to a NSNumber.
cell.textLabel.text = [scoreArray objectAtIndex:indexPath.row];
Try this instead:
cell.textLabel.text = [[scoreArray objectAtIndex:indexPath.row] stringValue];