Solved

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

Posted on 2011-09-18
6
926 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
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 
LVL 5

Accepted Solution

by:
mad_mac earned 500 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

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

Let Bitmoji into your life. Now is the time to learn a new language of smartphone messaging with this brief introduction.
Set up iPhone and iPad email signatures to always send in high-quality HTML with this step-by step guide.
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

706 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

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now