Solved

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

Posted on 2011-09-18
6
932 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
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

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

iCloud Drive was introduced after iOS 8 was launched last year. This drive is Apple’s online storage device that lets users sync their files and access them from all their Apple devices.   There is a lot of data that is not automatically backed up…
Are you having trouble connecting or getting your iPhone / Samsung device(s) to sync with Microsoft Exchange Server?   What have you tried?   What haven't you tried?
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.
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…

685 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