Stop NSXMLParser Instance from Causing _NSAutoreleaseNoPool - iphone

In my iPhone application, I have an instance of NSXMLParser that is set to a custom delegate to read the XML. This is then moved into its own thread so it can update the data in the background. However, ever since I have done this, it has been giving me a lot of _NSAutoreleaseNoPool warnings in the console. I have tried to add a NSAutoreleasePool to each of my delegate classes, however, this hasn't seemed to solve the problem. I have included my method of creating the NSXMLParser in case that is at fault.
NSURL *url = [[NSURL alloc] initWithString:#"http://www.mywebsite.com/xmlsource.xml"];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
CustomXMLParser *parser = [[CustomXMLParser alloc] init];
parser.managedObjectContext = self.managedObjectContext;
parser = [parser initXMLParser];
[xmlParser setDelegate:parser];
[NSThread detachNewThreadSelector:#selector(parse) toTarget:xmlParser withObject:nil];
If anyone has any ideas to get rid of this problem, I would really appreciate it.
Thanks.

In objective-c each thread needs its own NSAutorelease pool to handle autoreleased objects. So in your parse method you need to create (and release) NSAutoreleasePool object:
- (void)parse{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
...
// your method implementation
...
[pool release];
}

Related

can't figure out unrecognized selector sent to instance for XMLparser

I had a function XMLParser working but I'm trying to expand the class to handle different XML files my app needs.
I'm getting the error "Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[XMLParser initXMLParserForValidation]: unrecognized selector sent to instance 0x7564ea0'" Here's the code there.
- (void)validateEmail:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
NSXMLParser *nsXmlParser = [[NSXMLParser alloc] initWithData:data];
XMLParser *parser = [[XMLParser alloc] initXMLParserForValidation];
[nsXmlParser setDelegate:parser];
BOOL wasSuccessful = [nsXmlParser parse];
if (wasSuccessful) {
self.result = [parser result];
}
}
I've put breakpoints and stuff and but doesn't even get into my initXMLParserForValidation class. Here it is anyway, though.
- (XMLParser *) initXMLParserForValidatation {
self = [super init];
_result = [[ValidationResult alloc] init];
return self;
}
I've tried to mimic the code that's working but I can't see any differences. Driving me nuts. I'm new at this iphone stuff, though. Help much appreciated.
There's a spelling error in the declaration of your class' init method:
initXMLParserForValida*ta*tion
Then you're calling the init method like this, with the proper spelling, which doesn't exist:
initXMLParserForValidation
Remove the extra ta and you should be good to go!

How refresh UIVIewController to NSOperationQueue

I have a view where I show an UIImageView which complimentary internet, I use to bring data NSXMLparser which are loaded correctly, the problem was that I use to make the parser NSOperationQueue background so then I refresh the image in my main view. image which form no refresh them in any way
I leave here the code below
- (void)viewDidLoad
{
[Base64 initialize];
[super viewDidLoad];
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(loadDataWithOperation)
object:nil];
[queue addOperation:operation];
[operation release];
}
- (void) loadDataWithOperation {
getData=NO;
NSURL *url1 = [ NSURL URLWithString:[url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSData *xmlData = [NSData dataWithContentsOfURL:url1];
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:xmlData];
[parser setDelegate:self];
[parser parse];
[self performSelectorOnMainThread:#selector(showImage)withObject:nil waitUntilDone:NO];
}
-(void)showImage
{
NSArray *imagen =[[NSArray alloc] initWithArray:xml];
NSString *hola1 =[[imagen objectAtIndex:5]objectForKey:#"imagen"];
NSData * dataa = [Base64 decode:hola1];
img = [UIImage imageWithData:dataa];
self.images.image = img;
[images setImage:img];
[img release];
}
What am I doing wrong? appreciate your help please
I don't see anything obvious (though obviously we don't see the building of the xml array nor do we know precisely what the XML itself looks like). You should NSLog (or set a breakpoint and manually inspect) the xml as well as hola1 and dataa results in showImage, to identify at precisely what's going on at each step.
There are two possible problems with background operations and downloading XML. Neither of these seem applicable here, but I'll mention it just in case:
But there's nothing that would prevent the downloading and parsing of your XML like you have demonstrated here. If you were using NSURLConnection to download the XML (perhaps you simplified it here for the purposes of demonstration), there are issues in using NSURLConnection in a background queue. But if you use dataWithContentsOfURL (or better, NSXMLParser method initWithContentsOfURL) that wouldn't be an issue.
It looks like you're downloading a single XML, but if you were downloading multiple XML sources simultaneously, you should appreciate that many servers impose a limit as to how many concurrent connections they'll allow from a single client. You can use NSOperationQueue property maxConcurrentOperationCount to mitigate that problem. Four is a typical value.
Unrelated, but there are a couple of minor memory management things you might want to look at:
if you already have xml you don't need to create a new imagen;
you should probably release the queue object after adding the operation, or if you need to keep it around to reuse it, you should make it a class property or instance variable;
you could get rid of operation completely if you did:
[queue addOperationWithBlock:^{
[self loadDataWithOperation];
}];
you should release the parser object when you're done with it;
if you keep imagen in your code you should also release it when done; and
you should not release the img object since imageWithData returns an autorelease object
The routine memory management stuff probably would be pointed out to you if you did a static analysis (choose "Analyze" from the "Product" menu).
One final observation:
I notice that you have:
NSData *xmlData = [NSData dataWithContentsOfURL:url1];
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:xmlData];
Clearly that xmlData is not necessary because you could have also just have done:
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url1];

XMLParser leak instrument leak

NSURL *url = [[NSURL alloc] initWithString:#"http://www.someurl.com/sample.xml"];
xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[url release];
XMLParser *parser = [[XMLParser alloc] initXMLParser]; //50.0%
[xmlParser setDelegate:parser];
parser = nil;
[parser release];
[xmlParser parse]; //50.0%
[xmlParser release];
This is my parsing code and the leaks instrument is showing leaks. I really dont know what's wrong or how to fix this. Any suggestions?
parser = nil;
[parser release];
...this does not do what you think it does. Assuming parser is a property, then self.parser = nil and parser = nil do very different things. The former will call the parser setter method, which will release the old value and set the property to nil. The latter just changes the pointer from its current location to nil.
By setting the pointer to nil you have lost the reference to the object, so you've instantly leaked the object that was previous assigned to it - you are basically trying to release a nil object. You need to remove the nil call, or place it after the release (see below).
You may be thinking of setting a pointer to nil after you have released it, to prevent problems should you try and access it at some point in the future.
Here are a few other questions to help provide some context:
release Vs nil -- Best Practice
Difference between release and release then set to nil
I have had similar issues with using NSXMLParser, but found an easy fix to resolve the memory leak.
Instead of doing
xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
Do the following
NSData *xmlData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
xmlParser = [[NSXMLParser alloc] initWithData:xmlData];
I was able to get rid of my memory leaks
There's a similar post here about leaks in the parser. I also have this issue. it's annoying but it's not a huge leak so I don't worry too much about it. Will see if iOS 5 fixed the problem (if it is indeed a known leak)
Edit: I'm now interested to see if I made the mistake above!

iPhone - Threading and Delegation

I'm running some code in a background thread to get a text file from a service. That code fires a delegate at some point. It throws as SIGABRT error once the delegate is being called and well, my concept doesn't sound convincing to me either.
The code running at the background thread:
- (void)FetchStores
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Fetch from service
NSString *serviceURL = #"http://../index.html";
NSURL *myURL = [NSURL URLWithString:serviceURL];
NSData *dataRep = [NSData dataWithContentsOfURL:myURL];
storesList = [[Stores alloc] init];
storesList.storesDelegate = self;
[storesList FetchWithNSData:dataRep];
[pool release];
}
The storesList object will fire a delegate once all the stores have been extracted from the service. The delegate is getting caught by a function at the main thread.
Do you have any suggestions what am I doing wrong ?
Thank you,
f.
When calling the delegate, somewhere, you should make the switch to the main thread.
Especially if somewhere, you are updating the UI based on the data fetched.
You can use
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
to make the switch.
Maybe like this:
storesList = [[Stores alloc] init];
storesList.storesDelegate = self;
[storesList performSelectorOnMainThread:#selector(FetchWithNSData:) withObject:dataRep waitUntilDone:TRUE];
In your case, you should use waitUntilDone:TRUE so that the FetchWithNSData method gets a chance to retain the data.
It seems quite likely that FetchWithNSData: does not retain the passed dataRep and the data gets deallocated on the next line where you drain the local autorelease pool?

Memory allocation in detached NSThread to load an NSDictionary in background?

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.