Solution for a leak in touchXML when no internet and using initWithContentsOfURL - iphone

There's a leaking CXMLDocument object shown in instruments everytime, a request to an XML is made to a webservice AND when there's no internet connection available. Here's my code:
NSString *path = ... some URL
NSURL *url = [NSURL URLWithString: path];
CXMLDocument *itemListParser; = [[CXMLDocument alloc] initWithContentsOfURL:url options:0 error:nil];
... other stuff ...
If we digg deeper and trace initWithContentsOfURL call then we will find this method in "CXMLDocument.m":
- (id)initWithContentsOfURL:(NSURL *)inURL encoding:(NSStringEncoding)encoding options:(NSUInteger)inOptions error:(NSError **)outError
{
if (outError)
*outError = NULL;
NSData *theData = [NSData dataWithContentsOfURL:inURL options:NSUncachedRead error:outError];
if (theData)
{
self = [self initWithData:theData encoding:encoding options:inOptions error:outError];
}
else
{
[self release]; //My suggested fix: We need to release an alloc'ed object because after the "self = null" it will be unable to release it. See the info below.
self = NULL;
}
return(self);
}
It appears, if theData is nil (for example no connection) then self will be nil, and so the result of the call to TouchXML initWithContentsOfURL will be nil too. So, in my code:
CXMLDocument *itemListParser; = [[CXMLDocument alloc] initWithContentsOfURL:url options:0 error:nil];
I'm alloc'ing a memory but init returns nil, and so itemListParser becomes nil too. So later, trying to release the parser with [itemListParser release] does nothing because release is send to nil.
I was able to fix a leak by adding "[self release]" before "self = NULL" (see the line with my comment in TouchXML initWithContentsOfURL method)

Related

CHCSVParser - how to load from webserver

I would like to parse csv from webserver which gets updated everyday.I am using the csvparser from this link https://github.com/davedelong/CHCSVParser and I am using this code:
NSError *err = [[[NSError alloc] init] autorelease];
NSString *lunchFileURL = [[NSString stringWithFormat:#"http://www.somewhere.com/LunchSpecials.csv"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *lunchFile = [NSString stringWithContentsOfURL:[NSURL URLWithString:lunchFileURL] encoding:NSUTF8StringEncoding error:&err];
CHCSVParser *p = [[CHCSVParser alloc] initWithContentsOfCSVString:lunchFile usedEncoding:&encoding error:nil];
I get this error :
No visible #interface for 'CHCSVParser' declares the selector 'initWithContentsOfCSVString:usedEncoding:error:'
I checked this link Load remote csv into CHCSVParser and its not working .I am a noob to ios ,Please let me know how to fix this .Really Appreciate the help.Thanks in Advance.
It should probably be:
NSError *err;
NSString *lunchFileURL = [[NSString stringWithFormat:#"http://www.somewhere.com/LunchSpecials.csv"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *lunchFile = [NSString stringWithContentsOfURL:[NSURL URLWithString:lunchFileURL]
encoding:NSUTF8StringEncoding
error:&err];
// if you're going to create a CHCSVParser yourself, the syntax is:
CHCSVParser *p = [[CHCSVParser alloc] initWithCSVString:lunchFile
encoding:NSUTF8StringEncoding
error:&err];
// or, if you're going to use NSArray+CHCSVAdditions.h, the syntax might be:
NSArray *array = [[NSArray alloc] initWithContentsOfCSVString:lunchFile
encoding:NSUTF8StringEncoding
error:&err];
Note:
You don't need to alloc/init the NSError object; these classes that take a NSError ** parameter will create the autorelease error object for you if they encounter an error; otherwise they leave it alone;
The CHCSVParser class method is not initWithContentsOfCSVString, but rather initWithCSVString;
Alternatively, if you use the NSArray class extension, then the syntax is arrayWithContentsOfCSVString or initWithContentsOfCSVString; and
You specified an encoding of &encoding, but this parameter is not a pointer, so I don't see how that can possibly be right; I've just specified the encoding.
I assume you want to use the NSArray+CHCSVAdditions category method initWithContentsOfCSVString or arrayWithContentsOfCSVString (which gives you an autorelease object), not the CHCSVParser, but it's up to you.

iOS API in Parse - One to Many relation

I am using parse for the backend part of my iPhone App.
We can have a one to many relation in Parse which described in Relational Data.
This code works fine to retrive the data:
PFQuery *query = [PFQuery queryWithClassName:#"Comment"];
PFObject *fetchedComment = [query getObjectWithId:#"0PprArjYi3"];
NSString *content = [fetchedComment objectForKey:#"content"];
printf("%s", [content UTF8String]);
But when I use their codes provided in the Link, it returns null:
PFObject *post = [fetchedComment objectForKey:#"parent"];
[post fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) {
title = [post objectForKey:#"title"];
}];
printf("%s", [title UTF8String]); // RETURN NULL
Can anybody tell me what is wrong in this code? The problem could be fetchedcomment.
Addenda
This one also got an exception:
PFQuery *query = [PFQuery queryWithClassName:#"Comment"];
PFObject *fetchedComment = [query getObjectWithId:#"0PprArjYi3"];
PFObject *post = [fetchedComment objectForKey:#"parent"];
NSString *title = [post objectForKey:#"title"];
printf("%s", [title UTF8String]);
The method fetchIfNeededInBackgroundWithBlock is, as the name suggests, performed in background. Your code asks for a fetch and then immediately continues to the printf. At that point in time, the data hasn't been fetched yet, so title is probably still nil.
You have two options -
Use a synchronous method, such as [post fetch]: instead of [parent fetchInBackground...].
[post fetch];
title1 = [post objectForKey:#"title"];
Print the title inside the block that gets executed when the fetchInBackground... method finishes.

NSJSONSerialization not updating data

I am trying to parse JSON with iOS5's NSJSONSerialization. This code CORRECTLY parse the data I wanted for the first time, BUT THEN THE DATA JUST STAYED THE SAME. The JSON on the URL already changed, but the code kept giving me the first data it parsed, which is now incorrect. No matter how many times I "build and run", it keeps giving me the same thing.
When I copied the code to the new project, it works, again, for the first time, and then does the same thing.
I have no idea where the problem is, maybe cache?
Thanks for you help!!!
- (void)fetchedData:(NSData *)responseData {
NSError* error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:responseData
options:kNilOptions
error:&error];
NSArray* allDepartures = [json objectForKey:#"departures"];
NSLog(#"departures: %#", allDepartures);
NSDictionary* stops = [allDepartures objectAtIndex:0];
NSNumber* time = [stops objectForKey:#"expected_mins"];
NSString* name = [stops objectForKey:#"headsign"];
nameLabel.text = [NSString stringWithFormat:#"%#",name];
timeLabel.text = [NSString stringWithFormat:#"%i",[time intValue]];
}
- (IBAction)getInfo:(id)sender {
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL: myURL];
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:data waitUntilDone:YES];
});
How do you set "myURL", only in viewDidLoad? After a while if the OS calls viewDidUnload and you run the app again, viewDidLoad is called and myURL is set again. If thats the case you should set myURL every time you call getInfo.

Data gets deallocated before use with ASIHTTPRequest asynchronous mode

I'm using ASIHTTPRequest in asynchronous mode in a function called from my viewDidLoad()
NSURL *url = [NSURL URLWithString:#"http://somewebsite.com/data.txt"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
This is my requestFinished() where I do some text replacement and populate an NSArray with the data I've received from the website. It's just text, one data item per line.
The NSArray (and alloc / init) in viedDidLoad().
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSString *sTemp = [request responseString];
sTemp = [sTemp stringByReplacingOccurrencesOfString:#"\n" withString:#","];
arrayTidalData = [sTemp componentsSeparatedByString:#","];
NSLog(#"Loaded %d items into the array",[arrayTidalData count]);
[tableTideTimes reloadData];
}
NSLog reports 127 data items.
Now, I use the NSArray data to populate a UITableView.
But, in cellForRowAtIndexPath() when I attempt to access the NSArray (arrayTidalData), for instance by doing a count, I get the following:
TideTimes[14696:b303] * -[__NSArrayM count]: message sent to deallocated instance 0x4e6c6a0
(I turned on NSZOMBIEEnabled = YES to get this data)
It seems the NSArray has been deallocated before I can use it. I also tried populating an NSString with the data in requestFinish() but got the same result.
Am I missing something really simple or am I doing something terribly wrong?
(It's my first time with ASIHTTPRequest)
Replace
arrayTidalData = [sTemp componentsSeparatedByString:#","];
with
arrayTidalData = [[sTemp componentsSeparatedByString:#","] retain];
It is because componentsSeparatedByString: returns autoreleased object. So it is released after method requestFinished: ends working.
And don't forget to release at the end of work (for example, dealloc).

Need help with NSXMLPaser tutorial

At the last part of the tutorial I am having an issue with the users variable, its saying users variable is undeclared.
It is being declared in my NSXMLPaser class that I created as a NSMutableArray and I am "#import"ing the header file of NSXMLPaser class...
Here is the link to the tutorial I am working through, any help would be greatly appreciated.
http://wiki.cs.unh.edu/wiki/index.php/Parsing_XML_data_with_NSXMLParser
- (void) doParse:(NSData *)data {
NSString * filePath = [[NSBundle mainBundle] pathForResource:#"data" ofType:#"xml"];
NSData * fileData = [NSData dataWithContentsOfFile:filePath];
// create and init NSXMLParser object
NSXMLParser *nsXmlParser = [[NSXMLParser alloc] initWithData:fileData];
// create and init our delegate
XMLParser *parser = [[XMLParser alloc] initXMLParser];
// set delegate
[nsXmlParser setDelegate:parser];
// parsing...
BOOL success = [nsXmlParser parse];
// test the result
if (success) {
NSLog(#"No errors - user count : %i", [parser [users count]]); // users undeclared error here
// get array of users here
// NSMutableArray *users = [parser users];
} else {
NSLog(#"Error parsing document!");
}
[parser release];
[nsXmlParser release];
}
Either you use "[parser.users count]" like Babji said or you could use the dot notation for everything, like "parser.users.count".
Also, you could use "[[parser users] count]". This means you first get the collection users of the parser ("[parser users]") and then you call count on that collection ("[[parser users] count]").
use the users variable [parser.user count]