OK. I'm using NSXMLParser like so:
myParser = [[[BMLT_Parser alloc] initWithContentsOfURL:[NSURL URLWithString:uri]] retain];
[myParser setDelegate:self];
[myParser performSelectorInBackground:#selector(parse) withObject:nil];
I have my own subclass in order to do things like have a memory pool and some instance data. Basically, it's OK to think of BMLT_Parser as the same as NSXMLParser.
Note that I am calling it asynchronously, so there's no simple deallocation after a synchronous call.
What I have done, is declare the following delegate function:
- (void)parserDidEndDocument:(NSXMLParser *)parser ///< The parser in question
{
[myParser release];
myParser = nil;
}
myParser is an instance variable of the parser. Basically, myParser == parser in the callback.
Now, the problem is that Instruments tells me that the parser is leaking. Parsers leak badly, because they pack a lot of luggage.
How else should I dealloc asych parsers? I strongly suspect that I simply need to be directed to an "M", so that I can "RTFM".
Thanks!
myParser = [[[BMLT_Parser alloc] initWithContentsOfURL:[NSURL URLWithString:uri]] retain];
In the above code, u r firstly allocating memory for myParser by alloc, and again u r retaining .Here, u r doing wrong as u should retain only when u have take ownership of an object.But through alloc u will get ownership of the object"myParser".
And when u have used the object, u need to release that.
you should do like something this..
myParser = [[BMLT_Parser alloc] initWithContentsOfURL:[NSURL URLWithString:uri]];
[myParser setDelegate:self];
[myParser performSelectorInBackground:#selector(parse) withObject:nil];
[myParser release];
Again, in the delegate definition, you r firstly releasing the object then setting that to nil.This is quite meaningless ,as if u don't have memory for any object, how can we set any value to that. write something like this..
- (void)parserDidEndDocument:(NSXMLParser *)parser ///< The parser in question
{
if(_myParser)
{
[_myParser release];
}
}
Related
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];
While parsing of the twitter data, I used threads to call the main URL to download the data. It does the downloading perfectly but when I hit the back button while the data is downloading it throws performSelectorOnMainThread message deallocated. I know we can use isCancelled but its not working for me yet. Does anyone have come across this issue and have resolved it.
- (void)LoadTwitterData
{
NSString *urlString =#"http://search.twitter.com/search.json?q=tabc&result_type=recent&rpp=2500";
NSURL *url = [NSURL URLWithString:urlString];
NSString *jsonString = [NSString stringWithContentsOfURL:url];
NSDictionary *values = [jsonString JSONValue];
/**** Throws here *****/
[self performSelectorOnMainThread:#selector(didFinishLoadingResults:) withObject:values waitUntilDone:NO];
}
If you spin off a thread using a selector on self, you need to make sure that self is retained for the duration of that thread, otherwise (as in your case) self can be deallocated and your thread will try to call back into a zombie. The easiest way to do this is to pass self to the thread as an argument. If you use performSelectorInBackground:withObject: you should do something like this:
[self performSelectorInBackground:#selector(LoadTwitterData) withObject:self];
Or if you use NSThread you should pass self to the object: initializer argument.
In fact the safest way to use thread methods is to make the method static like this:
+ (void)LoadTwitterData:(id)arg
{
// ...
MyController *self = arg;
// ... do work
[self performSelectorOnMainThread:#selector(didFinishLoadingResults:)
withObject:values waitUntilDone:NO];
}
This way you are unable to access instance variables by accident which avoids various multi-threading issues. Any and all data the thread needs, including the self to callback to, should be passed in as 'arg', which can be an array or dictionary or whatever you need. This way you know that everything the thread needs will be retained for the duration of the thread, and because you aren't accessing instance variables through self, another thread can't go and change them around underneath you.
Another thing you should do in a thread method is wrap the whole thing with an autorelease pool:
- (void)LoadTwitterData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
#try {
// ...
} #finally {
[pool drain];
}
}
If LoadTwitterData: is in a background thread, you need to create an Auto release pool (If you haven't already). Surround your code with-
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
//your code
[pool drain];
It is normal to have such issues but I am currently stuck in knowing how it works.
Whenever I use NSXMLparser to parse the URL and store in the database, it gets parsed for the first time but when I parse it again it throws EXC_BAD_ACCESS.
Here is my code:
- (void) initParse {
[super init];
appleAppDelegate = (appleAppDelegate*)[[UIApplication sharedApplication] delegate];
NSURL *url = [[[NSURL alloc] initWithString:#"http:example.com/file.xml"] autorelease];
self.parser1 = [[[NSXMLParser alloc] initWithContentsOfURL:url] autorelease] ;
[parser1 setShouldResolveExternalEntities:NO];
[parser1 setDelegate:self];
[parser1 parse];
}
When it reaches the end of the function at "}", it throws EXC_BAD_ACCESS. I am not sure what is wrong since I am releasing my URL and even my parser.
Has any one come across this situation.
Sagos
Try running with NSZombieEnabled - that will tell you the type of the object which is being accessed after being freed.
You are accessing a released object which is exactly your problem, make sure you release at the end and make sure everything you need is still around.
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.
I have a strange issue, when it comes to parsing XML with NSXMLParser on the iPhone. When starting the app, I want to preload 4 table-views, that are populated by RSS-Feeds in the background.
When I init the table-views one-by-one, than loading, parsing and displaying all works like a charm. But when I try to init all view at once (at the same time), than it seems, that the XML-parser-instances are disturbing each other. Somehow data from one XML-Feed are "broadcasted" into other xml-parser instances, where they do not belong. Example: there is a "teammember" item, with "This is my name". When this bug occurs, there is a string from another xml-feed added, i.e. resulting in: "This is my name58", where 58 is the chart-position of something from the other view. "58" seems to miss then on the other instance.
It looks to me, that this bug occurs because of the NSXMLParser-delegate method:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (!currentStringValue) {
currentStringValue = [[NSMutableString alloc] initWithCapacity:50];
}
[currentStringValue appendString:string];
}
In this case "by coincidence" bytes are appended to strings, where they do not belong to.
The strange thing is, that every instance of NSXMLParser is unique, got its own unique delegates, that are attached to their own ViewController. Every parsing-requests spawns it own background-task, with its own (also also unique named) Autorelease-pool.
I am calling the NSXMLParser like this in the ViewController:
// prepare XML saving and parsing
currentStringValue = [[[NSMutableString alloc] initWithCapacity:50] retain];
charts = [[NSMutableArray alloc] init];
NSURL *url = [[NSURL alloc] initWithString:#"http://(SOME XML URL)"];
xmlParser = [[[NSXMLParser alloc] initWithContentsOfURL:url] retain];
//Set delegate
[xmlParser setDelegate:self];
//loading indicator
progressWheel = [[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(150.0,170.0,20.0,20.0)] autorelease];
progressWheel.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
[self.view addSubview:progressWheel];
[progressWheel startAnimating];
// start loading and parsing the xml-feed in the background
//[self performSelectorInBackground:#selector(parse:) withObject:xmlParser]; -> I also tried this
[NSThread detachNewThreadSelector:#selector(parse:) toTarget:self withObject:xmlParser];
And this is one of the background-tasks, parsing the feed:
-(void)parse:(NSXMLParser*)myParser {
NSAutoreleasePool *schedulePool = [[NSAutoreleasePool alloc] init];
BOOL success = [myParser parse];
if(success) {
NSLog(#"No Errors. xmlParser got: %#", myParser);
(POST-PROCESSING DETAILS OF THE DATA RETURNED)
[self.tableView reloadData];
} else {
NSLog(#"Couldn't initalize XMLparser");
}
[progressWheel stopAnimating];
[schedulePool drain];
[myParser release];
}
What could cause this issue? Am I calling the background-task in the right way? Why is this bug approaching, since every XML-Parser got its own, unique instance?
You should not be updating UI elements (like progressWheel) from inside a background thread. UI updates should be done on the main thread.
Use -performSelectorOnMainThread:withObject:waitUntilDone: to update UI elements from within a background thread.
I've released an open source RSS/Atom Parser for iPhone and it makes reading and parsing web feeds extremely easy.
You can set it to download the data asynchronously, or you could run it in a background thread synchronously to collect the feed data.
Hope this helps!