?
Solved

Objective C iPhone SBJson parser memory leek when data is loaded the second time

Posted on 2011-09-18
6
Medium Priority
?
960 Views
Last Modified: 2016-02-10
I have a problem in my iPhone application. I'm loading points into a Google map. These points are generated from a remote JSON file. Data is loaded without problems the first time I start the application but when I select a row in the pickerview and want to change the active pins after the program is startet i get a lot of memory leeks and the program also crashes if i continue doing it. It happens when i execute this method:
[self loadMainData:@"http://mysite.com/json.php"];

I don't know why this command works the first time i load the application when the viewDidLoad method is executed but not anymore. I guess i need to empty the arrays or release the data or something? I'm hoping you experts have an idea what needs to be done and how to do it.
#import "MapperViewController.h"
#import "MyAnnotation.h"
#import "JSON.h"

@implementation MapperViewController

@synthesize mapView, btnFilter, btnRoute;

-(void) viewDidLoad
{
    [super viewDidLoad];
	
	pickerViewArray = [[NSMutableArray alloc] init];
	
	[self loadMainData:@"http://mysite.com/json.php"];
}

-(void) getJsonData: (NSString*)JsonURL
{
	//Define raw JSON string
	NSString *MyRawJson = [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString: JsonURL]];
	
	if ([MyRawJson length] == 0) //If string doesn't contain any value
	{
		return; //Stop execution
	}
	
	SBJSON *parser = [[SBJSON alloc] init]; //Create parser
	list = [[parser objectWithString:MyRawJson error:nil] copy]; //Parse string
	
	[parser release];
	[MyRawJson release];
}

-(void) loadMainData: (NSString*)JsonURL
{
	[self getJsonData: JsonURL]; //Load JSON data using a method
	
	array0 = [list objectAtIndex:0]; //Define array0
	array1 = [list objectAtIndex:1]; //Define array1
	array2 = [list objectAtIndex:2]; //Define array2
	
	[mapView setDelegate:self];
	
	for(int i = 0; i < [array1 count]; i++)
	{
		//Add data to the picker view
		[pickerViewArray addObject:[array1 objectAtIndex:i]];
	}
	
    //[mapView setMapType:MKMapTypeStandard];
	[mapView setMapType:MKMapTypeHybrid];
    [mapView setZoomEnabled:YES];
    [mapView setScrollEnabled:YES];
	
    MKCoordinateRegion region = { {0.0, 0.0 }, { 0.0, 0.0 } };
	
	MyAnnotation *ann;
	
	//Get the number of pins
	int numberOfPins = [[array0 objectAtIndex:0] intValue];
	int numberOfCols = 4;
	int numberValue = 0;
	
	//Make loop to place pins
	for (int i = 0; i <= numberOfPins; i++)
	{
		if (i != 0)
		{
			ann = [[MyAnnotation alloc] init];
			
			numberValue = (numberOfCols * (i-1));
			
			region.center.latitude = [[array0 objectAtIndex:numberValue+1] floatValue]; //41.902245099708516;
			region.center.longitude = [[array0 objectAtIndex:numberValue+2] floatValue]; //12.457906007766724;
			ann.title = [NSString stringWithFormat:@"%@", [array0 objectAtIndex:numberValue+3]]; //@"Test title1";
			ann.subtitle = [NSString stringWithFormat:@"%@", [array0 objectAtIndex:numberValue+4]]; //@"Test subtitle1";
			
			[mapView setShowsUserLocation:NO];
			
			ann.coordinate = region.center;
			[mapView addAnnotation:ann];
			
			[ann release];
		}
	}
	
	region.center.latitude = 48.200001; 
	region.center.longitude = 12.300000; 
	region.span.longitudeDelta = 0.7f; 
	region.span.latitudeDelta = 0.7f;  
	[mapView setRegion:region animated:YES];
}

- (NSString *)pickerView:(UIPickerView *)thePickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component //This method asks for what the title or label of each row will be.
{
	return [pickerViewArray objectAtIndex:row]; //We will set a new row for every string used in the array.
}

- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component //And now the final part of the UIPickerView, what happens when a row is selected.
{
	/* Remove points from map start */
	NSArray *existingpoints = mapView.annotations;
	if ([existingpoints count] > 0)
	[mapView removeAnnotations:existingpoints];
	/* Remove points from map end */

	if (row != 0)
	{
		//Reload points using the selected values
		[self loadMainData:[NSString stringWithFormat:@"http://mysite.com/json.php?category_id=%i", [[array2 objectAtIndex:row] intValue]]];
	}
	else
	{
		//Reload points using the selected values
		[self loadMainData:@"http://mysite.com/json.php"];
	}
}

Open in new window

0
Comment
Question by:154115
  • 4
  • 2
6 Comments
 
LVL 5

Expert Comment

by:mad_mac
ID: 36559014
it looks like you have a lot of auto releasing going on with  the within the loadMainData method, you may want to consider setting up a auto release pool and draining it at the end of the loadMainData method.

in addition i think you need to look at your list variable, a simple retain rather than a copy may be better here, also your list var needs to be released and cleared before subsequent executions of the code in the getJsonData method.

if (list != nil) {
    [list release];
    list = nil;
}

Open in new window

0
 

Author Comment

by:154115
ID: 36562773
I used the code you attached but the application still crashes after a while if i select items in the picker so unfortunately it didn't solve the problem to release and clear the list.

"it looks like you have a lot of auto releasing going on with  the within the loadMainData method, you may want to consider setting up a auto release pool and draining it at the end of the loadMainData method."
Could you please tell me how to do that?

in addition i think you need to look at your list variable, a simple retain rather than a copy may be better here
list = [[NSArray alloc] init]; or what is the best way?
0
 

Author Comment

by:154115
ID: 36562838
If I uncomment the function call to the getJsonData method i get no error and the program will run without any problems (but with no data). For some reason this method is making the application crash but I don't know why.
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 5

Accepted Solution

by:
mad_mac earned 1500 total points
ID: 36563029
Ah, sorry....  Will do the simple one first..

To use an autorelease pool i would suggest doing the following.

-(void) loadMainData: (NSString*)JsonURL
{
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	
        [self getJsonData: JsonURL]; //Load JSON data using a method
	
	array0 = [list objectAtIndex:0]; //Define array0
	array1 = [list objectAtIndex:1]; //Define array1
	array2 = [list objectAtIndex:2]; //Define array2
        ...
        ...
        ...
	region.span.latitudeDelta = 0.7f;  
	[mapView setRegion:region animated:YES];
        [pool drain];
}

Open in new window


anything that is auto release will be released when the pool is drained.


on the other question i would suggest changing the code as below.

-(void) getJsonData: (NSString*)JsonURL
{

        if(list != nil) {
            [list release];
            list = nil;
        }

	//Define raw JSON string
	NSString *MyRawJson = [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString: JsonURL]];
	
	if ([MyRawJson length] == 0) //If string doesn't contain any value
	{
		return; //Stop execution
	}
	
	SBJsonParser *parser = [[SBJsonParser alloc] init]; //Create parser
	list = [parser objectWithString:MyRawJson error:nil]; //Parse string
	[list retain];

	[parser release];
	[MyRawJson release];
}

Open in new window


The list var is checked as the method is entered and released if it's already used, the object is passed back from the parser, however we need to retain this to ensure that it does not get deleted while we are still using it.
0
 

Author Comment

by:154115
ID: 36583366
Thank you mad_mac

It didn't solve the crash problem but it solved the memory issue. I found out i could load the data once and then load it from the arrays when i wanted to change it. Now the program don't crash anymore. Thank you for the good examples.They helped me a lot :-)
0
 

Author Closing Comment

by:154115
ID: 36583372
It solved the memory issue but the other part i solved myself. Thank you again mad_mac.
0

Featured Post

Vote for the Most Valuable Expert

It’s time to recognize experts that go above and beyond with helpful solutions and engagement on site. Choose from the top experts in the Hall of Fame or on the right rail of your favorite topic page. Look for the blue “Nominate” button on their profile to vote.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Let me explain this picture a little bit.  First, in case you haven't already guessed, you are looking at my 2 phones, an Android Samsung Galaxy S5 on the left and an iPhone 5 on the right.  They are on their respective cradles on my desk.  But, you…
Short answer to this question: there is no effective WiFi manager in iOS devices as seen in Windows WiFi or Macbook OSx WiFi management, but this article will try and provide some amicable solutions to better suite your needs.
This video demonstrates how to sync Microsoft Exchange Public Folders with smartphones using CodeTwo Exchange Sync and Exchange ActiveSync. To learn more about CodeTwo Exchange Sync and download the free trial, go to: http://www.codetwo.com/excha…
CodeTwo Sync for iCloud (http://www.codetwo.com/sync-for-icloud?sts=6554) automatically synchronizes your Outlook 2016, 2013, 2010 or 2007 folders with iCloud folders available via iCloud Control Panel. This lets you automatically sync them with…

840 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question