iOS - Unable to access NSMutableArray in didSelectRowAtIndexPath? - iphone

I am unable to access my NSMutableArray in didSelectRowAtIndexPath although i am able to access it in cellForRowAtIndexPath.
Here is my code :-
- (void)viewDidLoad
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"drinks" ofType:#"plist"];
self.drinkArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSLog(#"%#", self.drinkArray);
[super viewDidLoad];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
NSLog(#"I am inside cellForRowAtIndexPath");
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
NSDictionary *dict = [self.drinkArray objectAtIndex:indexPath.row];
cell.textLabel.text = [dict objectForKey:#"name"];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[dict release];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DrinkDetails *detailViewController = [[DrinkDetails alloc] initWithNibName:#"DrinkDetails" bundle:nil];
// ...
// Pass the selected object to the new view controller.
//detailViewController.drink = [self.drinkArray objectAtIndex:indexPath.row];
NSLog(#"%#", self.drinkArray);
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
Sometime NSLog prints some stupid output and sometime it gives me an error "EXC_BAD_ACCESS".
Please take a look and check what is wrong with my code.
Any help would be appreciated.
Thanks.

You have one (really two) issues, but the main one is in your -cellForRowAtIndexPath: method:
[dict release];
Get rid of this line and it should work just fine.
The reason why this fixes your issue is because -objectAtIndex: simply returns a pointer to the requested object in memory, therefore you do not (and should not) send the -release message to that object because the NSArray obtained ownership of the object when it was inserted. Sending -release to this object reference effectively deallocates the object in memory and now this indice in the NSArray is pointing to garbage memory. BAD BAD BAD
The other issue is that you have a memory leak here:
self.drinkArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
You are sending the -retain message to an object reference that you already have ownership of by way of sending -alloc. (This of course assumes that your #property has the retain setter modifier)
To fix this issue, simply send the -autorelease message to this instance:
self.drinkArray = [[[NSMutableArray alloc] initWithContentsOfFile:path] autorelease];

Don't release an object unless it was created with alloc or obtained with a method beginning with new or copy or explicitly retain'ed. (NARC)
In this case:
NSDictionary *dict = [self.drinkArray objectAtIndex:indexPath.row];
was not returned retained so you do not have ownership and should not release it.
By the same token:
self.drinkArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
is alloc'ed so it needs to be released or obtained with a convince method that will return an autoreleased object:
self.drinkArray = [NSMutableArray arrayWithContentsOfFile:path];

You shouldn't [dict release]in cellForRowAtIndexPath. objectAtIndex does not retain it, so it may be sweeped out of your array when you release it.

Related

self performSelector:#selector(loadData) withObject:nil - not working

I use:
self performSelector:#selector(loadData) withObject:nil...
it look like working with some command only in "loadData" but the rest are not.
Here is my viewdidload:
- (void)viewDidLoad
{
[super viewDidLoad];
[mActivity startAnimating];
[self performSelector:#selector(loadData) withObject:nil afterDelay:2];
//[mActivity stopAnimating];
}
and here is loadData:
-(void)loadData
{
[mActivity startAnimating];
NSLog(#"Start LoadData");
AppDelegate *delegate=(AppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *selectData=[NSString stringWithFormat:#"select * from k_proverb ORDER BY RANDOM()"];
qlite3_stmt *statement;
if(sqlite3_prepare_v2(delegate.db,[selectData UTF8String], -1,&statement,nil)==SQLITE_OK){
NSMutableArray *Alldes_str = [[NSMutableArray alloc] init];
NSMutableArray *Alldes_strAnswer = [[NSMutableArray alloc] init];
while(sqlite3_step(statement)==SQLITE_ROW)
{
NSString *des_strChk= [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement,3)];
if ([des_strChk isEqualToString:#"1"]){
NSString *des_str= [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement,4)];
[Alldes_str addObject:des_str];
}
}
Alldes_array = Alldes_str;
Alldes_arrayAnswer = Alldes_strAnswer;
}else{
NSLog(#"ERROR '%s'",sqlite3_errmsg(delegate.db));
}
listOfItems = [[NSMutableArray alloc] init];
NSDictionary *desc = [NSDictionary dictionaryWithObject:
Alldes_array forKey:#"description"];
[listOfItems addObject:desc];
//[mActivity stopAnimating];
NSLog(#"Finish loaData");}
it give me only printing 2 line, but did not load my Data to the table, but if I copy all the code from inside "loadData" and past in "viewDidLoad", it load the data to the table.
Any advice or help please.
A few things: If you see any NSLog output at all, then the performSelector is succeeding. You should change the title of your question.
If you are trying to load data into a table, the method should end with telling the UITableView to reloadData (or a more elaborate load using begin/end updates).
If the listOfItems is the data supporting the table, get this working first by hard-coding something like this:
-(void)loadData {
listOfItems = [NSArray arrayWithObjects:#"test1", #"test2", nil];
[self.tableView reloadData];
return;
// keep all of the code you wrote here. it won't run until you remove the return
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSString *string = [listOfItems objectAtIndex:indexPath.row];
cell.textLabel.text = string;
return cell;
// keep all of the code you probably wrote for this method here.
// as above, get this simple thing running first, then move forward
}
Good luck!

UITableView ObjectAtIndex

I'm developing an iPhone app that is a tabViewController with one of the tabs including a tableView. Objects in the table can be touched to go to a splash screen with information for that specific object.
Here's how I set up the array tableView:
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//each table view cell has its own identifier.
static NSString *cellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [sitesArray objectAtIndex:row];
cell.imageView.image = [imagesArray objectAtIndex:row];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
return cell;
}
- (void)viewDidLoad {
NSArray *pageHolder = [[NSArray alloc] initWithObjects:#"CSLP", #"NSLS", #"LSPA", #"SIOP", #"Transfer", nil];
self.pages = pageHolder;
[pageHolder release];
NSArray *sites = [[NSArray alloc] initWithObjects:#"CSLP", #"NSLS", #"LSPA", #"SIOP", #"Transfer Vision Program ", nil];
self.sitesArray = sites;
UIImage *cslp = [UIImage imageNamed:#"CSLP LOGO.png"];
UIImage *nsls = [UIImage imageNamed:#"NSLS LOGO.png"];
UIImage *lspa = [UIImage imageNamed:#"LSPA LOGO.png"];
UIImage *siop = [UIImage imageNamed:#"SIOP LOGO.png"];
UIImage *transfer = [UIImage imageNamed:#"TRANSFER LOGO.png"];
NSArray *images = [[NSArray alloc] initWithObjects: cslp, nsls, lspa, siop, transfer, nil];
[sites release];
self.imagesArray = images;
UIButton* modalViewButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[modalViewButton addTarget:self action:#selector(modalViewAction:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *modalBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:modalViewButton];
self.navigationItem.rightBarButtonItem = modalBarButtonItem;
[modalBarButtonItem release];
[self.table reloadData];
}
And here's my didSelectRowAtIndexPath method
-(void) tableView:(UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath
{
NSInteger row = [indexPath row];
NSMutableDictionary *rowData = [table objectAtIndex:indexPath.row];
UIViewController *targetViewController = [rowData objectForKey:kViewControllerKey];
if (!targetViewController)
{
// The view controller has not been created yet, create it and set it to our menuList array
NSString *viewControllerName = [[pages objectAtIndex:indexPath.row] stringByAppendingString:#"ViewController"];
targetViewController = [[NSClassFromString(viewControllerName) alloc] initWithNibName:viewControllerName bundle:nil];
[rowData setValue:targetViewController forKey:kViewControllerKey];
[targetViewController release];
}
SSARC_App_2AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate.orgsNavigationController pushNavigationItem:targetViewController animated:YES];
[delegate release];
}
The application crashes at the first call to objectAtIndex in the didSelectRowAtIndexPath method. The console states:
010-11-22 12:50:17.113 SSARC App
2[43470:207] -[UITableView
objectAtIndex:]: unrecognized selector
sent to instance 0x601e800 2010-11-22
12:50:17.116 SSARC App 2[43470:207]
* Terminating app due to uncaught exception
'NSInvalidArgumentException', reason:
'-[UITableView objectAtIndex:]:
unrecognized selector sent to instance
0x601e800'
* Call stack at first throw:
Now, I have a warning that says "UITableView may be unresponsive to ObjectAtIndex". I'm assuming this is the main issue here, however I'm unsure how to solve it. Interestingly enough I've searched for this issue everywhere, and cannot find it anywhere, so I'm curious to know if anyone has run into this problem and how to solve it. Any advice would be great. Let me know if you need more information.
I've been doing some coding on my own and I've gotten it to compile without any errors or warnings, but it still crashes. Currently this is what I have:
NSMutableDictionary *rowData = [self.menuList objectAtIndex:indexPath.row];
UIViewController *targetViewController = [rowData objectForKey:kViewControllerKey];
if (!targetViewController)
{
// The view controller has not been created yet, create it and set it to our menuList array
NSString *viewControllerName = [[pages objectAtIndex:indexPath.row] stringByAppendingString:#"ViewController"];
targetViewController = [[NSClassFromString(viewControllerName) alloc] init];//WithNibName:viewControllerName bundle:nil];
NSLog(#"targetViewController: %#", targetViewController);
[rowData setValue:targetViewController forKey:kViewControllerKey];
[targetViewController release];
}
SSARC_App_2AppDelegate *delegate = (SSARC_App_2AppDelegate*)[[UIApplication sharedApplication] delegate];
[[delegate orgsNavigationController] pushViewController:targetViewController animated:YES];
[delegate release];
delegate = nil;
The issue that the debugger reports is that 'viewControllerName' is 'Out of Scope'. I don't really understand what that means because when I use NSLog, viewControllerName is initialized. Does that mean it cannot find the class I'm referring to? Please let me know.
Assuming table is your UITableView, the call to [table objectAtIndex:indexPath.row] is completely nonsensical. It looks to me like you should be calling [pages objectAtIndex:indexPath.row] instead.
objectAtIndex should be called either for NSMutableArray Or NSArray. You cann't call it from UITableview Or CFDictionary Or CFString. So if you gets any warning Solve the warning first and then proceed further.

iPhone UITableView populateing from connectionDidFinishLoading

I have been trying for hours to figure this out. I have some JSON from a NSURLConnection. This is working fine and I have parsed it into an array. But I can't seem to get the array out of the connectionDidFinishLoading method. I an am getting (null) in the UITableViewCell method. I am assuming this is a scope of retain issue, but I am so new to ObjC I am not sure what to do. Any help would be greatly appreciated.
Cheers.
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
SBJSON *json = [[SBJSON alloc] init];
NSDictionary *results = [json objectWithString:responseString];
self.dataArray = [results objectForKey:#"data"];
NSMutableArray *tableArray = [[NSMutableArray alloc] initWithObjects:nil];
for (NSString *element in dataArray) {
//NSLog(#"%#", [element objectForKey:#"name"]);
NSString *tmpString = [[NSString alloc] initWithString:[element objectForKey:#"name"]];
[tableArray addObject:tmpString];
}
}
- (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.
//cell.textLabel.text = [self.tableView objectAtIndex:indexPath.row];
cell.textLabel.text = [self.tableArray objectAtIndex:indexPath.row];
NSLog(#"%#", tableArray);
return cell;
}
Solution:
After taking kubi and st3fan's advice with self.tableArray I found I had to reload the tableView with [self.tableView reloadData];
Get rid of the [connection release] in the 2nd line. The connection object comes in autoreleased, so this could cause crashes.
It looks like you've got a property named tableArray? If so, you're redeclaring the name in this method (you should have gotten a compiler warning).
On second thought, here's how the 2nd 1/2 of the method should look:
NSMutableArray *tmpArray = [[NSMutableArray alloc] initWithObjects:nil];
for (NSString *element in dataArray) {
[tmpArray addObject:[element objectForKey:#"name"]];
}
self.tableArray = tmpArray;
[tmpArray release];
In connectionDidFinishLoading: you use declare tableArray as a local variable. Therefore it will never be assigned to self.tableArray.
I had the same problem and I resolved it reloading the table.
I inserted this line at the end of connectionDidFinishLoading function
[self.tableView reloadData];
and it works.

UITableView isn't loading the data from my plist file what am i doing wrong?

Okay, so I have an app that I'm trying to get to populate a UITableView with the contents of a plist file. I haven't been able to find anyone else having problems doing this and how they fixed it so I'm probably just an idiot but I need to know how all the same!
I'm sure the error is somewhere in my viewDidLoad section or my tableview section...but maybe someone else can give me a better idea.
-(void)viewDidLoad {
NSString *path = [[NSBundle mainBundle] pathForResource:#"datam" ofType:#"plist"];
NSArray *array = [[NSArray alloc] initWithContentsOfFile:path];
self.listData = array;
[array release];
[super viewDidLoad];
}
#pragma mark -
#pragma mark Table View Data Source Methods
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [self.listData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *SimpleTableIdentifier = #"Identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:Identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SimpleTableIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [listData objectAtIndex:row];
return cell;
}
I think [super viewDidLoad] should be foremost in the viewDidLoad method.
NSString *path = [[NSBundle mainBundle] pathForResource:#"datam" ofType:#"plist"];
NSArray *array = [[NSArray alloc] initWithContentsOfFile:path];
This seems a little odd; is your file named "datam.plist"?
Try using some NSLogs() throughout your code to see what the status of your array is at any given time:
NSLog(#"%# has %i elements",ARRAY_OBJECT,[ARRAY_OBJECT count]);
Check the GDB output in XCode while the application runs to see if the results match what you expect.
self.listData = array; [array release];
Try this instead:
self.listData = [array copy]; [array release];
NSArray objects are pointers; when you set self.listData = array, you really tell anything looking for listData to look in the memory address originally occupied by array. When you release array, it vacates that memory address, thus making the pointer point to nothing.

Copy NSMutablearray to another

I am trying to copy NSMutableArray to another but it does not show me anything at the UITableView:
NSMutableArray *objectsToAdd= [[NSMutableArray alloc] initWithObjects:#"one",#"two"];
NSMutableArray *myArray = [[NSMutableArray alloc] initWithObjects:objectsToAdd,nil];
NSMutableArray *list = [[NSMutableArray alloc] init];
[self.list addObjectsFromArray:myArray];
Nothing show up! What is wrong?
It crashes my app because I do not have nil at my NSMutableArray how can I add nil to it? addobject:nil does not work it crashes the app:
static NSString * DisclosureButtonCellIdentifier =
#"DisclosureButtonCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
DisclosureButtonCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier: DisclosureButtonCellIdentifier]
autorelease];
}
NSUInteger row = [indexPath row];
NSString *rowString =nil;
rowString = [list objectAtIndex:row];
cell.textLabel.text = rowString;
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
[rowString release];
return cell;
Your initial call to alloc an NSMutableArray will most likely crash, since you don't have a nil terminator in your argument list.
Also, you have a local variable, list, and a property, list. Make sure you're instantiating what you think you are. You might need to do this:
NSMutableArray *objectsToAdd= [[NSMutableArray alloc] initWithObjects:#"one",#"two", nil];
NSMutableArray *myArray = [[NSMutableArray alloc] initWithObjects:objectsToAdd,nil];
self.list = [[NSMutableArray alloc] init];
[self.list addObjectsFromArray:myArray];
This might help you:
NSMutableArray *result = [NSMutableArray arrayWithArray:array];
or
NSMutableArray *result = [array mutableCopy]; //recommended
There are a few problems... One problem is that you're using 'initWithObjects' and adding the previous array. This seems like unwanted behaviour since you most likely want to add the strings #"one" and #"two" to the array. You most likely intended to use initWithArray or addObjectsFromArray. (Doing it the way you did, will add the NSMutableArray (not its objects) to the list)
The second problem, when you use initWithObjects, you need to list each of the objects and then terminate the list with a nil value. (docs) In other words, you need to use...
NSMutableArray *objectsToAdd = [[NSMutableArray alloc] initWithObjects:#"One", #"Two", nil];
The problem might be that the local declaration of listin line 4 conflicts with the property.