Solved

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

Posted on 2011-09-18
6
937 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
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!

 
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

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!

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
iPhone notifications disappear when I unlock 5 63
Jailbreak availability for new iPhone 7 Plus 2 88
Enterprise WhatsApp 1 38
Multiple Calendars on iOS devices? 9 41
Learn new improvements released by Google for Google Calendar. Noted in this article are simple tips and tricks that can make your everyday use of Google Calendar better.
After hours on line I found a solution which pointed to the inherited Active Directory permissions . You have to give/allow permissions to the "Exchange trusted subsystem" for the user in the Active Directory...
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.
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…

734 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