I am building an application that has to handle the parsing of several different kinds of XML files. As I want to standardize the procedure as much as possible, I created a singleton class which handles the parsing of any XML data. The class is handed two options, a unique identifier telling it what kind of XML data it is going to parse and the data itself. Within the class, there is the following function which does the parsing and returns an NSMutableArray object containing the results:
- (NSMutableArray*) initAPIDataParse:(NSData *)data APIRequestType:(int)requestType {
// Init parser
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
// Set delegate for parser
if (requestType == kXMLParserTypeOne) {
[parser setDelegate:[[[XMLParserOne alloc] init] autorelease]];
} else if (requestType == kXMLParserTypeTwo) {
[parser setDelegate:[[[XMLParserTwo alloc] init] autorelease]];
} // etc.
// let's parse the XML
[parser parse];
[parser release];
return lastParsedDict; //lastParsedDict is the NSMutableArray object returned from the Parser delegate
}
Above code works like a charm, except that if you parse the same type of XML multiple times this line leaks (which makes sense):
[parser setDelegate:[[[XMLParserOne alloc] init] autorelease]];
I have tried several things to resolve this issue like creating an instance variable with an instance of the delegate and releasing it whenever the same type of XML Parser was asked for again, yet it didn't work.
I am glad for any help here, thanks a lot!
Delegates objects are never retained. So using an auto-released object for them is mostl likely going to end up in a crash.
I refactored your code:
- (NSMutableArray*) parseData: (NSData*) data withAPIRequestType: (int) requestType
{
NSMutableArray* result = nil;
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
if (parser != nil)
{
switch (requestType) {
case kXMLParserTypeOne:
delegate = [XMLParserOne new];
break;
case kXMLParserTypeTwo:
delegate = [XMLParserTwo new];
break;
}
if (delegate != nil)
{
[parser setDelegate: delegate];
[parser parse];
result = [delegate.result retain];
[delegate release];
}
[parser release];
}
return [result autorelease];
}
Related
I want to fetch JSON data asynchronously. The data is set up in a way that one request will bring only 8 records. I need to send the requests repeatedly until the response becomes empty or returns less than 8 records.
Currently, I have these methods in myviewcontroller.m class:
(void)myCallback:(id)sender {
MyDataRequest *objMyDataRequest = [[[MyDataRequest alloc] init] autorelease];
objMyDataRequest.myRequiredVariableToGetAuthTokenDataResponse = classOfMyCallBack.someVariable;
// Initiate getAuthToken request
[objWishListRequest initiateGetAuthTokenRequest:self requestSelector:#selector(getAuthTokenDataResponse:)];
}
Now here is the definition of getAuthTokenDataResponse:
(void) getAuthTokenDataResponse:(NSData *)data {
NSString *stringResponse = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
SBJsonParser *parser=[[SBJsonParser alloc]init];
NSDictionary *objDictionaryForStringResponse = [parser objectWithString:stringResponse];
[stringResponse release];
[parser release];
MyListRequest *objMyListRequest = [[[MyListRequest alloc] init] autorelease];
objMyListRequest.myRequiredValueToGetMyDataResponse = [objDictionaryForStringResponse objectForKey:#"Data"];
// Initiate GetMyDataResponse request
[objMyListRequest initiateGetMyDataRequest:self requestSelector:#selector(getMyDataResponse:)];
}
(void) getMyDataResponse:(NSData *)data {
NSString *stringResponse = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
SBJsonParser *parser=[[SBJsonParser alloc]init];
NSDictionary *objGetMyDataRootDictionary = [parser objectWithString:stringResponse];
[stringResponse release];
[parser release];
NSDictionary *dataElements=[objGetMyDataRootDictionary objectForKey:#"Data"];
Wish *objMyData;
for (NSDictionary* objGetMyDataRootDictionary in dataElements) {
objMyData = [[Wish alloc]init];
//add different elements from dataElements into member variables of object objWish
[self.myDataArray addObject:objMyData];
[objMyData release];
}
[self.myDataTableView reloadData];
}
This method lies in MyDataRequest class:
(void)initiateGetMyDataRequest:(id)requestDelegate requestSelector:(SEL)requestSelector{
// Set the delegate and selector
self.delegate = requestDelegate;
self.callback = requestSelector;
NSString* unescapedUrlString = [NSString stringWithFormat:#"http://test.mytesturl.com/core.svc/alldata/My/get/All/?token=%#&search=&page=1",myRequiredtokenparameter];
[self request:url];
}
I need to send multiple requests to the same url (with different parameter value i.e. value of page number) to fetch the results. How may I achieve it given the above scenario? The calls must be asynchronous.
How should I make the actual flow between all these calls? How may I get the data of "all the pages" asynchronously?
I think you are looking for a operation queue. I use ASIHTTPRequests in my apps and they work.
If you want to use this library, here's the link how to use it: Show UIActivityIndicatorView when loading NSString from Web
Currently, I have a class that is parsing XML and sending the dictionary that the XML is parsed to to a view controller.
Here is a snippet of the code that sends the dictionary to the other class (where "response" is the dictionary):
if ([elementName isEqualToString:#"SessionData"])
{
// We reached the end of the XML document
// dumps dictionary into log
NSLog(#"Dump:%#", [response description]);
// sends dictionary to the VC
CardSetupViewController *setup = [[CardSetupViewController alloc]init];
setup.response = self.response;
//checks
NSLog(#"%# lololololol", [setup.response description]); //THIS WORKS FINE!!
return;
}
At that point, the code works fine. That NSLog marked with //THIS WORKS FINE!! works... obviously. Here is the method in the ViewController:
- (BOOL)authorize //this
{
AddCard *addCard = [[AddCard alloc]init];
ServerConnection *connection = [[ServerConnection alloc]init];
//XMLParser *xmlParser = [[XMLParser alloc]initXMLParser];
//serverReturn posts the data and is the ACTUAL server response in NSData form
NSData *serverReturn = [connection postData:[addCard textBoxToXml:
[self nameOnCardGet]:
[self ccNumGet]:
[self expMoGet]:
[self expYrGet]:
[self cvvGet]:
[self zipGet]:
[self nickNameGet]:
[self pinGet]]];
//This takes the information from the server and parses it to "response"
//Creates and inits NSXMLParser Object
NSXMLParser *nsXmlparser = [[NSXMLParser alloc] initWithData:serverReturn];
//Create and init our delegate
XMLParser *parser = [[XMLParser alloc] initXMLParser];
//set delegate
[nsXmlparser setDelegate:(id <NSXMLParserDelegate>) parser];
//initiates self.response THIS MAY NOT BE NEEDED
//response = [[NSMutableDictionary alloc]init];
//parsing
BOOL success = [nsXmlparser parse];
//error catch testing
if (success) {
NSLog(#"No errors");
}
else {
NSLog(#"Error parsing document!");
}
//dump
NSLog(#"ZOMG CHECK DIS OUT%#", [response description]);
return NO;
}
Basically, the NSLog that states "ZOMG CHECK DIS OUT" is returning (null) and I can't figure out why. No compilation errors, it is a property/synthesize as well. Any ideas?
Thanks in advance. Oh, and please excuse my NSLog comments. I had to differentiate from different parts of the code, and I was in a good mood.
Edit: I am using Automatic Reference Counting. Don't worry, nothing is leaking.
In your first code block, you generate a CardSetupViewController and then leak it. It is unrelated to whatever object is running the second code block. I assume that your second view controller is from your NIB?
Note that you're also leaking your NSXMLParser.
Your [response description], whatever that is, is probably an autoreleased object that gets released before ZOMG CHECK DIS OUT. Retain it and see if that works. Don't forget to release it when you're done with it.
Anyone else experiencing crashes deep down in the iPhone libraries when NSXMLParser parses an xml containing errors? I thought it was supposed to call:
(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
but instead it crashes the entire app somewhere inside _xmlRaiseError.
Is anyone else experiencing this and is there a way to catch this, instead of having my program crash?
The error handling is not found in the TouchXML framework or in the CXMLDocument. It is in the libxml framework which will (to my knowledge) output a string but not raise an exception. So it is all about passing an error pointer and then reading it straight after. If it is not nil, an error has occurred. If you are getting crashes the error should be somewhere else... Hope that helps.
You should be able to use #try/#catch to wrap this if you need to handle all kinds of malformed XML.
The XML parser never crashes for me, but my handlers have crashed on occasion. For example if I get < foo /> and try to store the value of it in an array (nil, boom). The following is the exact code I use, which parses XML using a delegate that I've made.
NSData *data = [[NSData alloc] initWithContentsOfFile:filename];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:data];
MGXMLParser *parser = [[MGXMLParser alloc] initWithRecipient:self];
[xmlParser setDelegate:parser];
BOOL success = [xmlParser parse];
if (success) {
NSLog(#"No errors");
} else {
NSError *error = [xmlParser parserError];
NSLog(#"Errors with xmlParser: %#", [error localizedDescription]);
}
[parser release];
[xmlParser release];
[data release];
MGXMLParser is my own class which is a delegate for the XML parser, in case it wasn't obvious.
Update: oops, SO parsed my < foo/ > into nothingness.
The problem is probably that your XML string gets autoreleased before parseErrorOccurred ever gets called, causing a call to a dealloc'd object.
The solution is do to something like:
NSData *data = [[contentString dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES] retain]; //use a retain, to stop data being autoreleased
NSXMLParser* xmlParse = [[NSXMLParser alloc] initWithData:data];
[xmlParse setDelegate:self];
[xmlParse parse];
[data release]; //now release data
[xmlParse release];
I filed this as a bug report and Apple answered me 1 year later to say it should be fixed in iOS5.
i have this
NSXMLParser *xmlParserf = [[NSXMLParser alloc] initWithContentsOfURL:url];
// NSLog(#"URL%#",urlf);
//Initialize the delegate.
XMLParser *parserf = [[XMLParser alloc] initXMLParser];
[xmlParserf setDelegate:parserf];
//Start parsing the XML file.
BOOL successs = [xmlParserf parse];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSLog(#" this xml is %d",[xmlParserf retainCount]);// getting error
NSLog(#" this paaat is %d",[parserf retainCount]);// getting error
if(successs)
{
NSLog(#"ZONE IS PARSED");
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
else
{
NSLog(#"NOT PARSED!!!");
}
//[xmlParserf release]; not working
//[parserf release];
now i dont know when to release those objects these are running in some threads
everytime you alloc (or copy), you must either release or autorelease.
In this case:
NSXMLParser *xmlParserf = [[[NSXMLParser alloc] initWithContentsOfURL:url] autorelease];
and
XMLParser *parserf = [[[XMLParser alloc] initXMLParser] autorelease];
It means that you keep it in memory at least until the end of the current function. If other objects hang on to it (i.e. retain it) then the objects stay in memory, until they are released (by those other objects).
I am trying to launch a background thread to retrieve XML data from a web service. I developed it synchronously - without threads, so I know that part works. Now I am ready to have a non-blocking service by spawning a thread to wait for the response and parse.
I created an NSAutoreleasePool inside the thread and release it at the end of the parsing. The code to spawn and the thread are as follows:
Spawn from main-loop code:
.
.
[NSThread detachNewThreadSelector:#selector(spawnRequestThread:)
toTarget:self withObject:url];
.
.
Thread (inside 'self'):
-(void) spawnRequestThread: (NSURL*) url {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[self parseContentsOfResponse];
[parser release];
[pool release];
}
The method parseContentsOfResponse fills an NSMutableDictionary with the parsed document contents. I would like to avoid moving the data around a lot and allocate it back in the main-loop that spawned the thread rather than making a copy. First, is that possible, and if not, can I simply pass in an allocated pointer from the main thread and allocate with 'dictionaryWithDictionary' method? That just seems so inefficient.
parseContentsOfResponse
-(void)parseContentsOfResponse {
[parser setDelegate:self];
[parser setShouldProcessNamespaces:YES];
[parser setShouldReportNamespacePrefixes:YES];
[parser parse];
NSError *parseError = [parser parserError];
if (parseError) {
NSLog(#"%#", parseError);
NSLog(#"publicID: %#", [parser publicID]);
NSLog(#"systemID: %#", [parser systemID]);
NSLog(#"line:%d column:%d", [parser lineNumber], [parser columnNumber]);
}
ready = YES;
}
First parse section
Each section creates element strings when its elementStart is signaled. The elementEnd will add the object to the dictionary and release the element. The remaining details are redundant and I think the point to note is that the allocations are not directed at an NSZone, therefore they should be residing in the thread's memory pool.
- (void)parserDidStartDocument:(NSXMLParser *)parser {
NSLog(#"%s", __FUNCTION__);
currentChars = [NSMutableString stringWithString:#""];
elementQuestion = [NSMutableString stringWithString:#""];
elementAnswer = [NSMutableString stringWithString:#""];
elementKeyword = [NSMutableString stringWithString:#""];
}
The simplest thing to do would be to create the dictionary in the separate thread, then set it as a property on the main thread, like so:
- (void)spawnRequestThread: (NSURL*) url {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
//do stuff with dict
[self performSelectorOnMainThread:#selector(doneWithThread:) withObject:dict waitUntilDone:NO];
}
- (void)doneWithThread:(NSDictionary *)theDict {
self.dict = theDict; //retaining property, can be an NSDictionary
}
Do you need to change the contents of the dictionary over time? If so, allocating on the main thread and changing the contents in the other thread is possible, but you have to worry about thread-safety issues--NSMutableDictionary isn't thread-safe, so you'd have to use an atomic property and locks:
//.h
#property (retain) NSMutableDictionary *dict; //don't use "nonatomic" keyword
#property (retain) NSLock *dictLock;
//.m
- (id) init {
//blah blah
dict = [[NSMutableDictionary alloc] init];
dictLock = [[NSLock alloc] init];
return self;
}
- (void)spawnRequestThread: (NSURL*) url {
//whenever you have to update the dictionary
[self.dictLock lock];
[self.dict setValue:foo forKey:bar];
[self.dictLock unlock];
}
Locking is quite expensive and inefficient in any case, so I'd tend to prefer the first method (I'm not sure which is more expensive, exactly, but the first is simpler and avoids thread-safety issues).
Also, looking at your code, it looks like your NSXMLParser is an ivar which you directly access. This is a bad idea, since NSXMLParser isn't thread-safe--I would recommend implementing it as a local variable instead. If you do need it as an ivar, use an atomic property and locks and only access it through accessors.