- (void)main
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
self.workingArray = [NSMutableArray array];
self.descriptionString = [NSMutableString string];
NSXMLParser *parser = [[[NSXMLParser alloc] initWithData:dataToParse] autorelease];
[parser setDelegate:self];
[parser parse];
if (![self isCancelled])
{
// notify our AppDelegate that the parsing is complete
[self.delegate didFinishParsing:self.workingArray];
}
self.workingArray = nil;
self.descriptionString = nil;
self.dataToParse = nil;
[pool drain];
}
in this code when we [parser parse] is called in this line the memory leaks problem is happened.
Related
I dont know why this NSXMLParser parse method is leaking.
I looked at the other similar SO question, but couldn't resolved it.
Here is my code.
- (void)parseXMLFileAtURL {
self.results = [[NSMutableArray alloc] init];
NSURL *xmlURL = [NSURL URLWithString:#"http://www.dukascopy.com/swiss/video/rss/"];
NSData *dataXml = [[NSData alloc] initWithContentsOfURL:xmlURL];
NSXMLParser *MyrssParser = [[NSXMLParser alloc] initWithData:dataXml];
[dataXml release];
[MyrssParser setDelegate:self];
[MyrssParser setShouldProcessNamespaces:NO];
[MyrssParser setShouldReportNamespacePrefixes:NO];
[MyrssParser setShouldResolveExternalEntities:NO];
[MyrssParser parse]; // memory leak here
MyrssParser.delegate=nil;
[MyrssParser release];
if(!imagesArray)
{
imagesArray = [[NSMutableArray alloc] initWithCapacity:[self.results count]];
for(int i=0;i<[results count];i++)
{
UIImage *image = [UIImage imageNamed:#"nophoto.png"];
[imagesArray addObject:image];
bImgExist[i] = NO;
}
}
}
Even After releasing my NSXMLParser object instrument still shows memory leak.
What I am missing here..
self.results = [[NSMutableArray alloc] init];
Properties take ownership (according to their declarations) of their assigned values. So the array you set this property to is retained by self (I'm assuming the property is either retain or copy here), but already has a retain count of +1 from its initialization.
Change the line to:
self.results = [NSMutableArray array];
And the memory leak should clear up.
My app is working fine, but when I run instrument for checking for leaks, it shows me a leak at this line of code, in purple with a 100.0% mark:
xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
Here's the method containing this line:
-(NSString*) languageSelectedStringForKey:(NSString*) key
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"zh" ofType:#"lproj"];
if(selectedLanguage==French)
{
FinalString = [[NSString alloc] initWithFormat:#"http://www.xyz.com/api_com.php?page_id=%d",IDValue];
url = [[NSURL alloc] initWithString:FinalString];
}
else if(selectedLanguage==German)
{
FinalString = [[NSString alloc] initWithFormat:#"http://www.x.com/api_com.php?page_id=%d",IDValue];
url = [[NSURL alloc] initWithString:FinalString];
}
else if(selectedLanguage==Nepali)
{
FinalString = [[NSString alloc] initWithFormat:#"http://www.xy.com/api_com.php?page_id=%d",IDValue];
url = [[NSURL alloc] initWithString:FinalString];
}
xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[url release];
//Initialize the delegate.
parser = [[NewsParser alloc] initXMLParser];
//Set delegate
[xmlParser setDelegate:parser];
//Start parsing the XML file.
BOOL success = [xmlParser parse];
if(success)
NSLog(#"No Errors");
else
NSLog(#"Error Error Error!!!");
NSBundle* languageBundle = [NSBundle bundleWithPath:path];
NSString* str=[languageBundle localizedStringForKey:key value:#"" table:nil];
return str;
}
Here's my ViewDidLoad method from which languageSelectedStringForKey is called.
- (void)viewDidLoad
{
// Do any additional setup after loading the view from its nib.
appDelegate = (ProgAppDelegate *)[[UIApplication sharedApplication] delegate];
IDValue = 1;
textLabel.text=[self languageSelectedStringForKey:#"Welcome to Advance Localization"];
[super viewDidLoad];
}
What is causing this leak, and how can I fix it?
this is dealloc method:-
- (void)dealloc
{
[xmlParser release];
[parser release];
[nibLoadedCell release];
[super dealloc];
}
Do you ever call
[xmlParser release];
?
If not, you should release it when you no longer need it. Perhaps in the dealloc method of the same class in which that line appears.
You need to make NewsParser parser an instance variable and release it in the dealloc. Above, you init it, but you don't release it. Of course, you can't because it's a delegate of xmlParser. So, to make sure the object is retained, then properly released, it must be an ivar.
You never release FinalString (at least not in the code you posted)
this is held in the URL, which is held by the parser :)
Also, have you considered what would happen if this function is called twice?
Each time you say
xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
you would leak the previous xmlParser ;)
If you are allocating to an instance variable, you have to remember to release the previous object i.e.
[xmlParser release];
xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
I have strange problem with my app.
When I open my app it parses an RSS feed and displays the results in a tableview and there are no leaks BUT if I press my refresh button it leaks (a lot) :(
My Project: Link
Code:
viewDidLoad
UIBarButtonItem *ShareButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:#selector(tableView_ReFresh)];
self.navigationItem.rightBarButtonItem = ShareButton;
[ShareButton release];
viewDidAppear
if ([stories count] == 0) {
NSURL *path = [[NSURL alloc] initWithString:#"http://feeds.feedburner.com/TheAppleBlog"];
[self parseXMLFileAtURL:path];
[path release];
}
tableView_ReFresh
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *path = [[NSURL alloc] initWithString:#"http://feeds.feedburner.com/TheAppleBlog"];
[self parseXMLFileAtURL:path];
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
[path release];
[pool drain];
reloadData
[blogTable reloadData];
parseXMLFileAtURL
stories = [[NSMutableArray alloc]init];
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
NSData *xml = [NSData dataWithContentsOfURL:URL];
rssParser = [[NSXMLParser alloc] initWithData:xml];
[rssParser setDelegate:self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[rssParser setShouldProcessNamespaces:NO];
[rssParser setShouldReportNamespacePrefixes:NO];
[rssParser setShouldResolveExternalEntities:NO];
[rssParser setDelegate:self];
[self.view setNeedsDisplay];
[rssParser parse];
[xml release];
didStartElement
currentElement = [[elementName copy]autorelease];
if ([elementName isEqualToString:#"item"]) {
// clear out our story item caches...
item = [[[NSMutableDictionary alloc] init]autorelease];
currentTitle = [[NSMutableString alloc] init];
currentDate = [[NSMutableString alloc] init];
currentContent = [[NSMutableString alloc] init];
currentPostID = [[NSMutableString alloc] init];
currentCommentLink = [[NSMutableString alloc] init];
currentCommentNum = [[NSMutableString alloc] init];
}
didEndElement
if ([elementName isEqualToString:#"item"]) {
// save values to an item, then store that item into the array...
[item setObject:currentTitle forKey:#"title"];
[item setObject:currentPostID forKey:#"link"];
[item setObject:currentContent forKey:#"Content"];
[item setObject:currentDate forKey:#"date"];
[item setObject:currentCommentLink forKey:#"commentRssLink"];
[item setObject:currentCommentNum forKey:#"commentsNum"];
[stories addObject:[item copy]];
NSLog(#"adding story: %#", currentPostID);
//NSLog(#"adding story: %#", currentContent);
}
foundCharacters
if ([currentElement isEqualToString:#"title"]) {
[currentTitle appendString:string];
} else if ([currentElement isEqualToString:#"guid"]) {
[currentPostID appendString:string];
} else if ([currentElement isEqualToString:#"content:encoded"]) {
[currentContent appendString:string];
} else if ([currentElement isEqualToString:#"pubDate"]) {
[currentDate appendString:string];
} else if ([currentElement isEqualToString:#"wfw:commentRss"]) {
[currentCommentLink appendString:string];
} else if ([currentElement isEqualToString:#"slash:comments"]) {
[currentCommentNum appendString:string];
}
parserDidEndDocument
[blogTable reloadData];
dealloc
[super dealloc];
[currentElement release];
[rssParser release];
[stories release];
[item release];
[currentTitle release];
[currentDate release];
[currentContent release];
[currentPostID release];
[currentCommentLink release];
[currentCommentNum release];
How do I fix this?
Thanks
My guess this is your leak:
stories = [[NSMutableArray alloc]init];
You're releasing stories in your dealloc, which I assume is a property, which I assume you have declared with retain. If I am correct, you should be able to fix this by either doing (A) self.stores = [[NSMutableArray alloc]init]; or (B) by putting [stories release]; before that line.
Assuming your version of Xcode is reasonably up-to-date, if you run your code with Build & Analyze I would guess it would catch this issue.
I think you need to read the documentation about memory management. First of each time you parse you are allocating the NSXMLParser again and then for each "item" element in the XML file you're allocating your strings again without releasing them.
The easiest route, without fully understanding the problem, is to create properties for your ivars and call 'self.ivar =' when you want to assign a new value to them. The generated setters will take care of releasing the memory that the variable was previously pointing to.
I strongly recommend that you read this though:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
How to call main thread ??? i can parse but i cant display data
- (void)viewDidLoad {
//self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.parentViewController.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"10.png"]];
[super viewDidLoad];
[NSThread detachNewThreadSelector:#selector(startTheBackgroundJob) toTarget:self withObject:nil];
}
- (void)startTheBackgroundJob {
NSUserDefaults *getida = [NSUserDefaults standardUserDefaults];
myIDa = [getida stringForKey:#"AppID"];
NSLog(#"#BOOK MARK ");
NSString *ubook = [[NSString alloc] initWithFormat:#"http://www.wapp=%#&action=show",myIDa];
NSLog(#" bookmark %#",ubook);
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
//NSString *outputString = [[NSString stringWithString:usearch] stringByAppendingString: UserText];
ubook = [ubook stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(#"My string is now = %#", ubook);
NSURL *url = [[[NSURL alloc] initWithString:ubook]autorelease];
//NSURL *url= [NSURL URLWithString:outputString];
NSLog(#" bookmark URL IS %#",url);
NSXMLParser *xmlParser = [[[NSXMLParser alloc] initWithContentsOfURL:url] autorelease];
//Initialize the delegate.
XMLParserbookm *parser = [[[XMLParserbookm alloc] initXMLParser]autorelease];
//Set delegate
[xmlParser setDelegate:parser];
//Start parsing the XML file.
BOOL success = [xmlParser parse];
if(success)
{
NSLog(#" xml parsed suucess");
//[super viewDidLoad];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
//[self searchTableView];
//mytimer4=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(wipe) userInfo:nil repeats:NO];
}
else{
NSLog(#"eeror");
}
[NSThread sleepForTimeInterval:3];
[self performSelectorOnMainThread:#selector(makeMyProgressBarMoving) withObject:nil waitUntilDone:NO]; // HOW TO CALL MAIN THREAD
[pool release]
}
You can try with
viewDidAppear:, this method is called after you go to a new view. Then at least, you can switch to new view, you should make sure that there is something on the screen in waiting for the xml parsing
Using Thread: You put parsing into another thread and then callback your main thread after you finish, then there will be no block at all
I have a viewController which imports XMLParser.h as the class xmlParser
I'm passing an NSURL object in my viewController to the xmlParser class with the getXML method below
goButton is the button I tap to call the getXML method below. I disable the button which I tapped to trigger the getXML method, but I'm not sure where to put the code to enable it again once the xmlParser has finished parsing the returned XML.
- (IBAction) getXML {
goButton.enabled = NO;
// allocate and initialize the xmlParser
xmlParser = [[XMLParser alloc] init];
// then generate the URL we are going to pass to it and call the fetchXML method passing the URL.
NSURL *xmlurl = [[NSURL alloc] initWithString:#"http://www.mysite.com/myfile.xml"];
[xmlParser fetchXMLFromURL:xmlurl];
// release objects
[xmlurl release];
[xmlParser release];
}
As per #Squeegy recommendation, I modified my code.
- (IBAction) getXML {
goButton.enabled = NO;
xmlParser = [[XMLParser alloc] init];
[self performSelectorInBackground:#selector(parseInBackground:) withObject:xmlParser];
}
- (void)parseInBackground:(XMLParser*)parser {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *xmlurl = [[NSURL alloc] initWithString:#"http://www.mysite.com/myfile.xml"];
[parser fetchXMLFromURL:xmlurl];
[self performSelectorOnMainThread:#selector(didFinishXMLParsing:) withObject:parser];
[xmlurl release];
[pool drain];
}
- (void)didFinishXMLParsing:(NSXMLParser*)parser {
goButton.enabled = YES;
}
Looks to be working until it gets to the line
[self performSelectorOnMainThread:#selector(didFinishXMLParsing:) withObject:parser];
The compiler complains as follows:
2010-02-17 00:22:20.574 XMLApp[2443:521b] *** -[viewController performSelectorOnMainThread:withObject:]: unrecognized selector sent to instance 0x1285a0
2010-02-17 00:22:20.578 XMLApp[2443:521b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[viewController performSelectorOnMainThread:withObject:]: unrecognized selector sent to instance 0x1285a0'
2010-02-17 00:22:20.583 XMLApp[2443:521b] Stack: (
861696817,
860329709,
861700631,
861203093,
861166272,
18715,
846004025,
845672609,
848189713
)
- (IBAction)getXML {
goButton.enabled = NO;
xmlParser = [[XMLParser alloc] init];
NSURL *xmlurl = [[NSURL alloc] initWithString:#"http://www.mysite.com/myfile.xml"];
[xmlParser fetchXMLFromURL:xmlurl];
[self performSelectorInBackground:#selector(parseInBackground) withObject:xmlParser];
[xmlurl release];
[xmlParser release];
}
- (void)parseInBackground:(NSXMLParser*)parser {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[parser parse];
[self performSelectorOnMainThread:#selector(didFinishXMLParsing:)
withObject:parser
waitUntilDone:NO];
[pool drain];
}
- (void)didFinishXMLParsing:(NSXMLParser*)parser {
goButton.enabled = YES;
}
The trick is to do the processing on a background thread, which allows the UI to do stuff. When parsing is done, you have to make any UI changes back on the main thread.
When the parser finishes parsing, it will call it's delegate's:
- (void)parserDidEndDocument:(NSXMLParser *)parser
In that method, you can re-enable the button. You should probably do so with a performSelectorInMainThread call, since it involves changing a view.