My didSelectRowAtIndexPath function causes memory leak - iphone

My first post here. Seems I have run into a bit of a memory leak problem.
I have a tab bar with two columns, one for salad and one for sushi. In each tab I also have a tableview that displays the different food choices. When the user presses one of the cells the app takes the user to a more detailview that displays a larger image and some info about the chosen food menu.
But when I run leaks I get a 98,5% leak on the following line.
[self.navigationController pushViewController: detail animated: YES];
if I release detail after this line, the app crashes.
Here is the rest of the function code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *message = nil;
NSMutableString *image_string = nil;
DetailViewController *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"detailview"];
if(tableView == SushiTable)
{
message = [sushiNames objectAtIndex:indexPath.row];
image_string = [NSMutableString stringWithString:[sushiImages objectAtIndex:indexPath.row]];
}
if(tableView == SaladTable)
{
message = [saladNames objectAtIndex:indexPath.row];
image_string = [NSMutableString stringWithString:[saladImages objectAtIndex:indexPath.row]];
}
[image_string deleteCharactersInRange: [image_string rangeOfString: #"_small"]];
NSMutableString *temp_str = [[message copy] autorelease];
NSString *final = [[[temp_str stringByReplacingOccurrencesOfString:#" " withString:#"_"]
stringByReplacingOccurrencesOfString:#"รค" withString:#"a"]
lowercaseString];
detail.food_name = message;
detail.image_name = image_string;
detail.food_info_key = final;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[self.navigationController pushViewController: detail animated: YES];
detail = nil;
}
EDIT:
I saw also that in my viewDidLoad function in DetailViewController the following line has a memory leak, don't know if it's maybe related.
food_image.image = [UIImage imageNamed:image_name];
EDIT2:
Also I'm running on the simulator.

Try it.
[self.navigationController pushViewController: detail animated: YES];
[detail release];
detail = nil;
When you push an object.Now it is added into navigation stack array, Now array owner of this object
so you should need release object which you are added in an array.

Declare the detail globally and release it in dealloc:. It may work.

Related

Load a DetailViewController in TableViewcontroller

I just followed this tutorial:
Implementing UITableView Sections from an NSArray of NSDictionary Objects
This is a good example. But I want to add more fonctions. I want to load a detailView. I followed more than 5 tutorials to do it, but I fail every time. After 4 hours, i'm asking you.
How can I load a detailView with this sample?
Regards, Fanny
Already tried, nowi have this code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *selectedRow = nil;
NSDictionary *dictionary = [self.books objectAtIndex:indexPath.row];
selectedRow = [dictionary objectForKey:#"Title"];
//Initialize the detail view controller and display it.
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:#"DetailView" bundle:[NSBundle mainBundle]];
dvController.selectedRow = selectedRow;
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
dvController = nil;
But nothing appen :/
Hope this tutorial help you.
http://www.xcode-tutorials.com/uitableview-%E2%80%93-2-loading-a-detail-view/

UITableView with JSON help

I am trying to feed in some JSON data to my iPhone app, the data is coming in fine as I have NSLog's telling me so.
The problem I am having is trying to get the results to show in a UITableView. I have a navigation controller underneath a tab bar controller, the navigation controller contains a table view controller which loads another NIB file with a table view connected to a class which is the delegate and data source delegate.
I also need to categorize the results into sections - these being
England
Scotland
Wales
N.Ireland
To get an idea of what JSON string I am using see this one.
As you can see the JSON does not cater for the sections but I am yet to implement this, so i would need to know beforehand so I do not have to amend much code later on.
OK - I am using Stig JSON parser.
Here is my ListVenuesView.h (connected to table view)
#import <UIKit/UIKit.h>
#import "SBJson.h"
#interface ListVenuesView : UITableViewController <UITableViewDelegate, UITableViewDataSource> {
IBOutlet UITableView *venueList;
NSMutableDictionary *jsonArray;
}
#property (nonatomic, retain) IBOutlet UITableView *venueList;
#property (nonatomic, retain) NSMutableDictionary *jsonArray;
#end
jsonArray is used to store the JSON data and eventually the proper array.
And here is my ListVenuesView.m (key areas in question)
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Table View Loaded");
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
// This is where we load the JSON data
NSURL *jsonURL = [NSURL URLWithString:#"http://www.thebigfishexperience.org.uk/sources/ajax/venue-json.php?location=peterborough"];
NSString *jsonData = [[NSString alloc] initWithContentsOfURL:jsonURL];
NSLog(#"%#", jsonData);
// Convert jsonData to array
self.jsonArray = [jsonData JSONValue];
NSLog(#"%#", jsonArray);
NSLog(#"count is: %i", [self.jsonArray count]);
// Release NSString and NSURL
[jsonURL release];
[jsonData release];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.jsonArray count];
}
- (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];
}
NSMutableDictionary *dict = [self.jsonArray objectAtIndex: indexPath.row];
cell.textLabel.font = [UIFont fontWithName:#"Arial" size:15.0];
cell.textLabel.text = [dict objectForKey:#"venueName"];
cell.detailTextLabel.text = [dict objectForKey:#"venueCordDist"];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
// Configure the cell...
return cell;
Also how can I use the data in the cells to go to another subview of the nav controller which gives me a back button and displays the info from the JSON string just for that particular cell that has been tapped.
I think this has something to do with it? Not sure though as this is my first app i am building! So probably expect more pleas of assistance - ha ! ;)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"Nib name" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
*/
}
On selecting a row, as mentioned by u, we are navigating to another view. Let us assume that the view controller is DetailViewController which is a sub-class of UIViewController.
In the DetailViewController.h , declare a NSDictionary object.
In DetailViewController.m, add
-(void)setVenueDict:(NSDictionary*)venueDict
{
if( _venueDict )
{
[_venueDict release];
}
_venueDict = [venueDict retain];
}
In ParentViewController, ur didSelectRow.. method should be like this.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"Nib name" bundle:nil];
// ...
// Pass the selected object to the new view controller.
NSDictionary *dict = [self.jsonArray objectAtIndex: indexPath.row];
[detailViewController setVenueDict:dict];
detailViewController.title = [dict objectForKey:#"venueName"];
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
In the second view controller, u can do whatever u want with the _venueDict.
Nathan,
since you want to reuse the data parsed from the JSON feed over more than one ViewController the best way to approach this is to build an object Model so that you can pass the object for the selected row in the list to the detail ViewController.
I would also separate the JSON parsing code into a separate class and not keep it in the ViewController.
You can find classes to fetch JSON on this link.
The result from the custom code to parse the JSON feed would give back a NSDictionary with as keys the section names you mention. And the value in the NSDictionary for those keys would be an array of your custom objects that contain all the relevant data for one row (and detail screen).
Hope this helps you on your way.
jsonArray is NSMutableDictionary.
have to use
[jsonArray objectForKey:key];
//check this line
NSMutableDictionary *dict = [self.jsonArray objectAtIndex: indexPath.row];
this may help.

Why is the author not doing a release on childController in the following segment of code?

I was doing a tutorial and the author shows an example however I am curious as to why he is not releasing childController at the end of the function. Any thoughts?
-(void)tableView:(UITableView *) tableView
accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath{
if (childController==nil) {
childController = [[DisclosureDetailController alloc] initWithNibName:#"DisclosureDetailController" bundle:nil];
}
childController.title=#"Disclosure Button Pressed"; //why this line?????
NSUInteger row = [indexPath row];
NSString *selectedMovie = [list objectAtIndex:row];
NSString *detailMessage = [[NSString alloc]initWithFormat:#"you pressed disclosure button for %#",selectedMovie];
childController.message = detailMessage;
childController.title = selectedMovie;
[detailMessage release];
[self.navigationController pushViewController:childController animated:YES];
}
It seems that childController is a field in this class and therefore (hopefully) releases it in the dealloc section.
He/She actually creates it only once.
Edit:
After a true nil check on childController the it gets a retain count of 1 and is assigned to the field. If the childController is not realeased prematurely this will be done only once during the lifetime of the instance of this whole class.
He's holding onto childController as an ivar, so presumably it will be released in the class's dealloc method.

UITable view: didselectrowatIndexPath for multiple rows

I have a table view(HomeViewController) consisting of items as:
locations >
Reporting >
Setting >
i am able to do this with the help of "didselectrowatIndexPath" for a single row but when i am trying to do so with multiple rows(if else construct), not getting error but still unable to click on any one (locations,reporting or setting).I have imported .h files of all three above.my code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([[menuList objectAtIndex:indexPath.row] isEqual:#"LOCATIONS"])
{
LocationViewController *locationViewController;
locationViewController = [[LocationViewController alloc] initWithNibName:#"LocationViewController" bundle:nil];
locationViewController.menuList = [menuList objectAtIndex:indexPath.row];
[self.navigationController pushViewController:locationViewController animated:YES];
}
else if([[menuList objectAtIndex:indexPath.row] isEqual:#"REPORTING"])
{
Reporting *reporting;
reporting = [[Reporting alloc] initWithNibName:#"Reporting" bundle:nil];
reporting.menuList = [menuList objectAtIndex:indexPath.row];
[self.navigationController pushViewController:reporting animated:YES];
}
//[locationViewController release];
}
Also want to discuss about release statement
help me!
Thanks
isEqual tests the object's equality to another object. If the strings in your menuList array are all in upper case then this is fine. If they're like they are in your example before the code then you're going to have problems. Also, if they're both NSStrings then you should use isEqualToString rather than isEqual. You can test this by doing something like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *arrayValue = [menuList objectAtIndex:indexPath.row];
NSString *myValue = #"LOCATION";
NSLog(#"array value: '%#' my value: '%#'",arrayValue,myValue);
}
The release is not valid because the object is "out of scope".
An objects scope is the current "visible" code base for that variable. Here are some examples:
- (void)aRandomFunction {
/* here is a variable/object. Its scope is the whole function because it has been
declared directly in the function. All constructs have access to it (within the function) */
NSString *myString = #"My String";
if(YES){
NSLog(#"%#", myString); // myString is visible here because its in scope.
}
}
- (void)anotherRandomFunction {
if(YES){
/* here, because we've declared the variable within the if statement
it's no longer a direct object of the function. Instead its a direct
child of the if statement and is therefore only "visible" within that
if statement */
NSString *myString = #"My String";
NSLog(#"%#", myString); // myString is visible here because its in scope.
}
NSLog(#"%#", myString); // but NOT available here because it is out of scope
}
So in essence, a variable's scope is its direct parent construct and all its parent's children constructs.
So there is two ways to do your example. My favourite is this way:
- (void)aFunctionToPushAViewController {
UIViewController *nextPage = NULL;
if(YES){
nextPage = [[CustomViewController alloc] initWithNibName:nil bundle:nil];
}
else {
nextPage = [[ADifferentViewController alloc] initWithNibName:nil bundle:nil];
}
[self.navigationController pushViewController:nextPage animated:YES];
[nextPage release];
}
or... you can just release it in the if statement...
- (void)aFunctionToPushAViewController {
if(YES){
CustomViewController *nextPage = [[CustomViewController alloc] initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:nextPage animated:YES];
[nextPage release];
}
else {
ADifferentViewController *nextPage = [[ADifferentViewController alloc] initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:nextPage animated:YES];
[nextPage release];
}
}

Problems sending NSString variables to a ViewController before it is pushed

I am having problems sending along 2 NSString variables to a view controller right before it is pushed.
here is some relevent code for pushing the controller:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
CommentsViewController *c = [[CommentsViewController alloc] initWithNibName:#"CommentsView" bundle:nil];
c.subreddit = subreddit;
c.redditId = [[[list objectAtIndex:indexPath.row] objectForKey:#"data"] objectForKey:#"id"];
c.title = [[[list objectAtIndex:indexPath.row] objectForKey:#"data"] objectForKey:#"title"];
[self.navigationController pushViewController:c animated:YES];
[c release];
NSLog(#"subreddit: %#", subreddit);
NSLog(#"redditId: %#", [[[list objectAtIndex:indexPath.row] objectForKey:#"data"] objectForKey:#"id"]);
}
note that printing out the variables via NSLog works as expected.
also, the c.title assignment seems to work fine, as i can see the title text on the pushed controller.
here(not just here, anywhere) is where i am finding (null) when printing the corresponding variables which i have assigned:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"subreddit: %#", subreddit);
NSLog(#"redditId: %#", redditId);
}
2010-12-30 20:06:17.108 RedditReader[1073:307] subreddit: (null)
2010-12-30 20:06:17.131 RedditReader[1073:307] redditId: (null)
subreddit has property set as: #property (nonatomic, retain) NSString *subreddit;
Why can't i seem to assign these values correctly so i can use them in the pushed view controller?
How can i assign these values so i can use them in the pushed view controller?
It seems like your viewDidLoad gets called before the variables are set (I don't use nib files so I can't say for sure...). You could make an init method that receives those variables, like this:
- (id)initWithTitle:(NSString *)aTitle reddit:(NSString *)aReddit ... {
self.title = aTitle;
// ...
if (self = [super initWithNibName:#"CommentsView" bundle:nil]) {
// ...
}
return self;
}
Hope this helps!
Override viewWillAppear and you should see your populated variables, viewDidLod is called when uniting from the nib