Memory leak when using json-data on a map - json

I've the following problem:
I'm accessing foursquares Venue API and get a JSON string back. I parse this string with this json-framework. After that I'm saving the dictionaries and arrays for accessing further information about the venues (in special I'm using the explore API). So the venue information is saved deeply (for my experience) in the json-structure tree. And after getting the needed information (venue name & coordinates) I put a corresponding pin on a map with the same coordinates and with the venue's name as the pin's name.
And exactly at the point where I want to set the pin's name, I get a memory leak. So something get's wrong here. If I don't set any title, all works fine. So the memory leak occurs only when I'm setting the name of the venue to the pin.
Here is the corresponding code fragment:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//Parse JSON string
// Store incoming data into a string
NSString *jsonString = [[NSString alloc] initWithData:self.fetchedJSONData encoding:NSUTF8StringEncoding];
[self.fetchedJSONData setLength:0];
// Create a dictionary from the JSON string
NSDictionary *results = [NSDictionary dictionaryWithDictionary:[jsonString JSONValue]];
[jsonString release];
NSDictionary *response = [NSDictionary dictionaryWithDictionary:[results objectForKey:#"response"]];
NSArray *groups = [NSArray arrayWithArray:[response objectForKey:#"groups"]];
NSDictionary *groupsDic = [groups lastObject];
NSArray *items = [NSArray arrayWithArray:[groupsDic objectForKey:#"items"]];
for (int i=0; i<[items count]; i++)
{
CLLocationCoordinate2D annotationCoord;
MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc] init];
NSDictionary* oneItemDoc = [NSDictionary dictionaryWithDictionary:[items objectAtIndex:i]];
NSDictionary *singleVenue = [NSDictionary dictionaryWithDictionary:[oneItemDoc objectForKey:#"venue"]];
/*
* Leak here in the next two lines!
*
*/
NSString *titleName = [[[singleVenue objectForKey:#"name"] copy] autorelease];
annotationPoint.title = titleName;
NSDictionary *locationOfVenue = [NSDictionary dictionaryWithDictionary:[singleVenue objectForKey:#"location"]];
annotationCoord.latitude = [[locationOfVenue objectForKey:#"lat"] doubleValue];
annotationCoord.longitude = [[locationOfVenue objectForKey:#"lng"] doubleValue];
annotationPoint.coordinate = annotationCoord;
[self.mapView addAnnotation:annotationPoint];
[self.annotationsArray addObject:annotationPoint];
[annotationPoint release];
}
}
So the leak occurs when I want to set the title for the annotationPoint.
For each venue fetched with JSON I get the following leak trace (blurred libraries are my own libraries):
Has anybody a suggestion how to solve this problem? I tried many, many things. So the key issue seems to be how to "hand over" the [singleVenue objectForKey:#"name"] correctly. I first tried to set it without a copy and an autorelease, but then I get a zombie object. So I don't know how to do this. I think the problem are not these two lines, but some lines above them. Am I right? I also have the suggestion, that my 3rd party json parser is forcing this problem (cf. leak trace).
So I hope someone can help me to fix this problem. Would be really great!
Update: The problem seems to be independent of the corresponding JSON parser. I've testet my code with another parser, same problem there. So it has to do something with my code itself.
I think I know what's the problem. So the leak occurs after closing the map. So after dealloc. So it might be, that I've missed something there. I have a mapview and I also release it in dealloc and set it to nil in viewDidUnload. I also release all the other Arrays etc. in dealloc. Is there something else (specific about the map and view) which I need to release? I think this might be the problem!
Update: Solved the problem: I had to set all Foursquare pins' title and subtitle to nil in the dealloc method, because a value (accessed via a JSON parser) was retained by the map view somehow. Now all works fine!

Solved the problem: I had to set all Foursquare pins' title and subtitle to nil in the dealloc method, because a value (accessed via a JSON parser) was retained by the map view somehow. Now all works fine!

Had a similar situation, annotation's (MKPointAnnotation) title not being released properly. Solved it by simply setting mapView to nil just before releasing it.
I think this is quite safer than calling an extra [title release], which also does work, but if mapView is fixed internally it would cause a problem.

Related

RestKit: How to use RKMappingOperation

I have a JSON structure retreived via web socket, that I want to apply to an existing managed object. The particular object to be modified is identified by one of the keys in the JSON. The JSON may not contain all the attributes, but I only want to update the attributes that are present in the JSON (not nullify the others).
I got some initial pointers on the RestKit IRC channel to use RKMappingOperation but am now stuck on the implementation.
First I tried this:
RKMappingOperation *mappingOperation = [[RKMappingOperation alloc] initWithSourceObject:parsedObject destinationObject:nil mapping:[MyManagedObjectClass customMapping]];
Since I don't have the instance of the object to be updated at hand, I passed nil to destinationObject, hopping the mapper would figure it out based on the mapping provided.
Alas, I get nil from mappingOperation's mappingInfo after performing the mapping (but no error).
[mappingOperation performMapping:&localError];
if (localError != nil) {
NSLog(#"%#", [mappingOperation mappingInfo]); // outputs nil
} else {
NSLog(#"error: %#", localError); // no error
}
So my hunch is that I do indeed need to get the managed object instance that I want updated and provide it to the mapping operation, but I can't figure out how. I tried using existingObjectWithID on the managed object context, passing it the ID in my JSON, but no luck. When passing that to the mapping operation i get a 'null' error.
Am I on the right track? What am I missing?
EDIT: After fiddling around some more, I realize the docs specify that you must provide a dataSource if destinationObject is set to nil. So here's what I tried next:
RKManagedObjectMappingOperationDataSource *mappingDS = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:[[[RKObjectManager sharedManager ] managedObjectStore] mainQueueManagedObjectContext] cache:[[[RKObjectManager sharedManager] managedObjectStore] managedObjectCache]];
mappingOperation.dataSource = mappingDS;
Embarrassingly, I had also mixed up the error conditional, so that's why I couldn't see the error on the previous attempt (without data source). Now it seems that the mapping operation performs successfully. Will report back if it's actually the case, and answer my own question :-)
The solution:
RKMappingOperation *mappingOperation = [[RKMappingOperation alloc] initWithSourceObject:parsedObject destinationObject:nil mapping:[MyManagedObjectClass customMapping]];
RKManagedObjectMappingOperationDataSource *mappingDS = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:[[[RKObjectManager sharedManager ] managedObjectStore] mainQueueManagedObjectContext] cache:[[[RKObjectManager sharedManager] managedObjectStore] managedObjectCache]];
mappingOperation.dataSource = mappingDS;
[mappingOperation performMapping:&localError];
if (localError != nil) {
NSLog(#"%#", [mappingOperation mappingInfo]);
} else {
NSLog(#"error: %#", localError);
}

Trouble loading data for TableView from server

Im using a web-service API to load information from a DB (mySQL). When transitioning to a UITableViewController, I retrieve an array that contains categories in the VieDidLoad method. However, I am not able to display the array of categories that I got into the table view cells. When I init the category NSMutubaleArray and hardcode it on the otherhand, the tableview does display the information. I am trying to understand what I'm doing wrong in the steps of loading this array and populating it onto the tableview. Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
//When I init the _categoryList the way below, it displays the info.
//_categoryList = [[NSMutableArray alloc] initWithObjects:#"Trending",#"Sports",#"Portraits", #"Art",#"IdUser", nil];
_toCategory = [[NSString alloc]init];
//When I try to populate it from the server it doesnt work.
_categoryList = [[NSMutableArray alloc]init];
//just call the "getCategories" command from the web API
[[API sharedInstance] commandWithParams:[NSMutableDictionary dictionaryWithObjectsAndKeys:
#"getCategories",#"command",
nil]
onCompletion:^(NSDictionary *json) {
//got stream
NSArray *tempArray = [json objectForKey:#"result"];
for (int i=0;i<tempArray.count;i++){
[_categoryList insertObject:[tempArray objectAtIndex:i] atIndex:i];
}
//I verify that I was able to retrieve the array with the correct conent.
[self printArray:_categoryList];
NSLog(#"got categories");
[self.tableView reloadData];
}];
}
Any help would be greatly appreciated. Thanks!
UPDATE #1:
The JSON return is an array with NSDictioanry's. I print out its contents just to make sure, by calling the JsonArray objectForKey[‘categories’] and get this:
SwiphtMVPClone[2410:11303] Array at Index 0 = Trending
SwiphtMVPClone[2410:11303] Array at Index 1 = Sports
SwiphtMVPClone[2410:11303] Array at Index 2 = Portraits
SwiphtMVPClone[2410:11303] Array at Index 3 = Art
Right now, my app is actually crashing – getting this error:
[__NSCFDictionary isEqualToString:]: unrecognized selector sent to instance 0x72a85d0

Understanding xcode json web request

this is my first time connecting an app to the web, I just want to make sure i am clear on how this works.
I want to download data from a mysql db hosted online, it also seems to have php attached (hostgator) is the host.
So steps are:
1. In php (on the server module), set up a method to transcribe mysql information into JSON using the attached PHP module. Return an array (json i think is a mutilayered dictionary array object)
In xcode, use apple's json framework to create a url request, and download data into a json object (or array or dictionary?
Go through the data and create objects and save to coredata.
Please let me know if im following the logic correctly.
Also does JSON return 1 object or all the objects on the mysql db. So if i need to input 10,000 objects on coredata i have to make 10,000 requests or one request and parse 10,000 objects worth of info?
Also is this the best way to do what i need to do? I have heard of http request but it seems complicated, and I have no clue what it is.
Sorry for such a noob question.
Thanks for the help.
There are many ways to do it, some are "more correct" than others :-) but you are on the right way.
I explain what I would do in a similar situation:
1- for the PHP engine, you should create an API to access your data. Which data?
The simpliest and probably the first thing you can do TO TEST (only for testing purpose!!!!) is to create a page that receive a query via POST from your ios APP and answer with an encoded JSON string. With JSON you can transfer an entire hierarchy of objects: variables, arrays, dictionary...is up to you to decide how link the objects together.
If the data you want to encode is a table, you can do something similar to this:
// first connect to the database and execute the query, then...
$data = array();
while(!$RS->EOF){
for ($i = 0; $i < $cols; $i++){
$rowName = utf8_encode($RS->Fields[$i]->name);
$rowValue = utf8_encode($RS->Fields[$i]->value);
$rowData[$rowName] = $rowValue;
}
array_push($data, $rowData);
$RS->MoveNext();
}
$response['data'] = $data;
echo json_encode($response);
the result is a JSON object with a first dictionary with a key named "data".
Inside the "data" key there is an array of dictionaries. Each dictionary has the column name for the key, and the column value for the data:
data = (
{
attivita = "this is the first row in the table";
id = 2548;
},
{
attivita = "this is the second row in the table";
id = 2547;
};
}
You can simply use json_encode to create your json string.
On the iPhone side, I suggest you to download and use AFNetworking. It's a very good and complete open source framework, with a lot of builtin objects and methods for http/https requests, XML, JSON ecc...
Using AFNetworking you can make the request in a similar way:
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:#"http://url.for.the.php.page"];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:mainPath parameters:params];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
// your success code here
// remember, the first object is a dictionary with a key called data
NSArray *arrayOfData = [JSON objectForKey:#"data"];
NSMutableArray *arrayOfActivities = [NSMutableArray array];
for (NSDictionary *objectDictionary in arrayOfData) {
// objectDictionary is your dictionary containing
// a key with the column name and a value with the column content
// here you can init your custom objects
}
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
// your failure code here
}];
[operation start];
As you can see, the JSONRequestOperationWithRequest: method has a completion handler returning you the decoded JSON, so you can access your dictionaries/arrays directly using objectForKey or objectAtIndex ;-)
Last suggestions: in a production and secure environment, you should avoid sending query over post requests. The code I pasted here is for a private application (used only by me for testing purpose). It's better to use a different API for each kind of request and a secure authentication method (look at oAuth).
I suggest you to give a look to the Instagram or Twitter API (Instagram is simpler), trying to use it. They will give you some ideas on how to create your own API.
Good luck
Your first step of creating a method to return JSON-ified MySQL data is correct. What exactly is returned however, is completely up to you! You will find that JSON is an extremely flexible format that allows you to put nearly any format of data in your response. It may make sense, depending on what you want to do, to have several methods in your PHP module that return different things. For example, if you have a database of books, maybe you want to create a method that allows you to specify an author, and will return a JSON response with all the books in your database written by that author.
You could use HttpRequest to actually perform the call to your module, but I found the Obj-C NSURLRequest quite easy to use:
NSURLRequest *request = [NSURLRequest requestWithURL:
[NSURL URLWithString:#"http://yoururl/books/38917"]];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
Don't think that you have to manually parse the JSON response into your own Objective-C objects; this is a very common task and there exist many great libraries to do so. Check out this tutorial on the SBJSON framework to view one of the most popular solutions.
As an example, here is how you could parse the JSON response into an NSDictionary for easy traversal:
// Create SBJSON object to parse JSON
SBJSON *parser = [[SBJSON alloc] init];
// parse the JSON string into an object - assuming json_string is a NSString of JSON data
NSDictionary *object = [parser objectWithString:json_string error:nil];

objective-c: creating a server method

Currently I am able to retrieve data from a MySQL db by using HTTP GET and an NSURL connection, but I need to know how to encapsulate this action into a method. Could somebody inform me of how to put the following code into a method so that I can call it throughout my app, and also so that I can show a UIActivityIndicator while the process is underway?
//somethign like this?
-(void)getDataFromServer:(NSString *)url{
//enter code here
}
My current server request: I am using this in didselectrowforindexpath, so that the following stuff is done whenever somebody selects a cell. The issue is that I keep re-using this same code in several different methods, so I assume that it is bad practice to keep copying/pasting this code rather than having a method.
// retrieve data
NSString *buildURL = [[NSString alloc] initWithFormat:#"http://myurl.com"];
// begin query
NSURL *url = [NSURL URLWithString:buildURL];
NSError *e;
NSString *jsonreturn = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&e];
NSLog(#"jsonreturn: %#",jsonreturn);
NSData *jsonData = [jsonreturn dataUsingEncoding:NSUTF32BigEndianStringEncoding];
NSError *error = nil;
EDIT:
Much better way to do this, which I just implemented, is to use ASIHTTPRequest. The library provides for all the needs which I was trying to accomplish with the method above.
Just take your whole second code block and put it inside the {} of the first code block. You probably want to return that jsonData variable instead of void, though.

How to extract app information from the Apple store?

I'm a newbie with Json, so I want to extract some app information from the app store, I found this question here :
How to get info from the Apple iTunes «App Store» and «Mac App Store»
It allows to get a Json file containing all the app information, but it would be nice if someone could show me how to get the price for example.
Thanks.
Where and how do you want to collect the results? Is this for an app or for some other kind of tool? In the reuslting JSON you have an array "results" where each element has a "currency" and a "price" value. Those can be queired rather simply. For example using the iOS 5 NSJSONSerialization (note this is an example, there has to be more error handling, handling of no / too many entries etc):
// assuming you have the downloaded data
NSError *error = nil;
NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:downloadedData options:nil error:&error];
if (error != nil)
{
// error handling
}
else
{
NSArray *results = [jsonData objectForKey:#"results"];
// assuming you have 1 valid entry
NSDictionary *result = [results objectAtIndex:0];
NSNumber *price = [result objectForKey:#"price"];
NSString *currency = [result objectForKey:#"currency"];
NSLog(#"Price is %# #%", price, currency);
}
Similar ways to access the data are avilable in other JSON frameworks, just grad the results array and every dictionary therein has a price and currency value.