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.
Related
I am trying to send a CSV file through MfMail Composer.Everything works fine but there are lot leaks while using Instruments.I am not able trace out where I went wrong.Here is my code.
-(NSData *)getCSV
{
NSManagedObjectContext *moc = [(ETAppDelegate *)[[UIApplication sharedApplication] delegate]managedObjectContext];
NSFetchRequest *request = [[[NSFetchRequest alloc]init]autorelease];
[request setEntity:[NSEntityDescription entityForName:#"Expense" inManagedObjectContext:moc]];
NSError *error = nil;
NSArray *results = [moc executeFetchRequest:request error:&error];
NSMutableArray *expensesList = [[[NSMutableArray alloc]init]autorelease];
for(int i = 0;i<[results count];i++){
NSString *category = [[results objectAtIndex:i]valueForKey:#"category"];
NSString *date = [[NSDateFormatter dateFormatterwithMediumStyle]stringFromDate:[[results objectAtIndex:i]valueForKey:#"date"]];
NSString *amount = [NSNumberFormatter localizedStringFromNumber:[[results objectAtIndex:i]valueForKey:#"amount"] numberStyle:NSNumberFormatterCurrencyStyle];
NSString *mailString = [NSString stringWithFormat:#"%#,%#,%#",category,date,amount ];
[expensesList addObject:mailString];
}
NSString *expensesString = [expensesList componentsJoinedByString:#"\n"];
NSData *expensesData = [expensesString dataUsingEncoding:NSUTF8StringEncoding];
return expensesData;
}
-(void)displayComposerSheet
{
NSData *csvFile = [self getCSV];
NSString *csvFileName = #"MyExpenses";
MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc]init];
[mailController setSubject:#"List Of Expenses"];
[mailController setMessageBody:#"Expenses" isHTML:NO];
[mailController addAttachmentData:csvFile mimeType:#"text/csv" fileName:csvFileName];
[mailController setMailComposeDelegate:self];
[self presentModalViewController:mailController animated:YES];
[mailController release];
}
I can't say that I see anything in the code you've provided that should cause you to leak. Your leaks are likely taking place elsewhere.
What I do see, however, is the potential to create a lot of autoreleased objects in that loop in -getCSV, and depending on how many iterations you're performing, that could be almost as bad. The way you've written it, I see a date formatter and three strings headed for your main autorelease pool with every iteration. One suggestion is to create a date formatter outside your loop that you can reuse inside it. Another suggestion is to frame the guts of your loop with a local autorelease pool; that will prevent your main autorelease pool from becoming too large.
Apropos of nothing, you should also consider using fast enumeration.
Here's your loop with the suggestions applied:
NSDateFormatter *myDateFormatter = [NSDateFormatter dateFormatterWithMediumStyle];
NSMutableArray *expensesList = [[[NSMutableArray alloc] init] autorelease];
for (id obj in results)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *category = [obj valueForKey:#"category"];
NSString *date = [myDateFormatter stringFromDate:[obj valueForKey:#"date"]];
NSString *amount = [NSNumberFormatter localizedStringFromNumber:[obj valueForKey:#"amount"] numberStyle:NSNumberFormatterCurrencyStyle];
NSString *mailString = [NSString stringWithFormat:#"%#,%#,%#", category, date, amount];
[expensesList addObject:mailString];
[pool release];
}
I am trying to load an image in the background from a url. The code works great if all I pass is the NSUrl. If I try to pass an NSArray with additional variables, it never gets called:
This code works great, LoadImage2 is called which in turn nicely calls ImageLoaded2.
- (void)LoadBackgroundImage2: (char*)pImageURL
{
NSString* pImageURLString = [NSString stringWithFormat:#"%s", pImageURL];
NSLog( #"LoadBackgroundImage2: %#", pImageURLString );
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(LoadImage2:)
object:pImageURLString];
[queue addOperation:operation];
[operation release];
}
- (void)LoadImage2: (NSString*)pImageURL
{
NSLog( #"LoadImage2: %#", pImageURL );
NSData* imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:pImageURL]];
UIImage* image = [[[UIImage alloc] initWithData:imageData] autorelease];
[imageData release];
[self performSelectorOnMainThread:#selector(ImageLoaded2:) withObject:image waitUntilDone:NO];
}
This code does not work. LoadImage never gets called:
- (void)LoadBackgroundImage: (char*)pImageURL :(int)textureID :(int)textureType
{
printf( "LoadBackgroundImage( %s, %d, %d)\n", pImageURL, textureID, textureType );
NSString* pImageURLString = [NSString stringWithFormat:#"%s", pImageURL];
NSArray* pUrlAndReferences = [[[NSArray alloc] initWithObjects: pImageURLString, textureID, textureType, nil] autorelease];
NSOperationQueue *queue = [[NSOperationQueue new] autorelease];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(LoadImage:)
object:pUrlAndReferences];
[queue addOperation:operation];
[operation release];
}
- (void)LoadImage: (NSArray*)pUrlAndReferences
{
NSString* pImageUrl = [pUrlAndReferences objectAtIndex: 0];
int textureId = [ [ pUrlAndReferences objectAtIndex: 1 ] intValue ];
int textureType = [ [ pUrlAndReferences objectAtIndex: 2 ] intValue ];
NSLog( #"\n\nLoadImage: %#, %d, %d\n", pImageUrl, textureId, textureType );
NSData* pImageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:pImageUrl]];
UIImage* pImage = [[[UIImage alloc] initWithData:pImageData] autorelease];
NSArray* pImageAndReferences = [[[NSArray alloc] initWithObjects: pImage, textureId, textureType, nil] autorelease];
[pImageData release];
[self performSelectorOnMainThread:#selector(ImageLoaded:) withObject:pImageAndReferences waitUntilDone:NO];
}
Anyone have any ideas why LoadImage doesn't get called?
Thanks.
My guess is your not retaining your queue. Here's what's happening
Your array (autoreleased) goes into the NSInvocationOperation and it's being retained (no problem)
Your NSInvocationOperation goes into the queue (retained) and then it's released. No problem here since the retain count is still one: 1 (alloc) + 1 (retain) - 1 (release) = 1 = not dealloced.
Your queue is alloced (new = alloc+init) and then autoreleased, but it's not being retained elsewhere. Here comes the problem: since you have autoreleased the queue, once the method LoadBackgroundImage is finished the queue has a retain count of 0 and it's automatically released, therefore, your Invocation won't be executed.
You can try if that's the problem by just removing the autorelease call from the queue. If I'm correct your code should work. But be aware that that is not a good solution because you're loosing memory. It's just to see if it works.
You should definitely make a class, a singleton, an instance variable or whatever you like to retain that instance of the queue. Also, it's a good idea to only have one queue for all the LoadBackgroundImage calls instead of creating a new queue every time.
- (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.
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];
Updates code
- (void) searchBarSearchButtonClicked:(UISearchBar *)theSearchBar {
mytimer3=[NSTimer scheduledTimerWithTimeInterval:.03 target:self selector:#selector(show) userInfo:nil repeats:NO];
NSLog(#" search is down");
//////////// rnd to hold keyboard
//ovController.view.backgroundColor = [UIColor grayColor];
self.tableView.scrollEnabled = NO;
UserText=[self.searchDisplayController.searchBar text];
myInt= UserText.length;
//int myInt= [save length];
// NSString *myStringPrt1 = [[NSString alloc] init];
// NSString *myStringPrt2 = [[NSString alloc] init];
if(UserText.length>3)
{
//[ovController.view removeFromSuperview];
//[tableView reloadData];
url=nil;
// myStringPrt1=#"http://find.php?keyword=";
NSString * myStringPrt2=UserText;
// myStringPrt1=[myStringPrt1 stringByAppendingString:myStringPrt2];
// myStringPrt1 = [myStringPrt1 stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// loadingView.hidden=NO;
NSString *outputString = [[NSString stringWithString:#"http://find.php?keyword="] stringByAppendingString: UserText];
outputString = [outputString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(#"My string is now = %#", outputString);
url= [NSURL URLWithString:outputString];
NSXMLParser *xmlParser = [[[NSXMLParser alloc] initWithContentsOfURL:url] autorelease];
//Initialize the delegate.
XMLParser *parser = [[[XMLParser alloc] initXMLParser]autorelease];
//Set delegate
[xmlParser setDelegate:parser];
//Start parsing the XML file.
BOOL success = [xmlParser parse];
if(success)
{
[super viewDidLoad];
[self searchTableView];
mytimer4=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(wipe) userInfo:nil repeats:NO];
}
}
}
Right, you have memory leaks all over the place:
NSString *myStringPrt1 = [[NSString alloc] init];
NSString *myStringPrt2 = [[NSString alloc] init];
url=nil;
myStringPrt1=#"http://wap?keyword="; //MEMORY LEAK, this will assign a new string to myStringPrt1, without releasing the first one
myStringPrt2=UserText; //MEMORY LEAK, this will assign a new string to myStringPrt2, without releasing the first one
myStringPrt1=[myStringPrt1 stringByAppendingString:myStringPrt2]; //MEMORY LEAK, this will assing an autoreleased string the myStringPrt1, without releasing the old one first.
myStringPrt1 = [myStringPrt1 stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; //MEMORY LEAK, this will assing an autoreleased string the myStringPrt1, without releasing the old one first.
I suggest you first read the memory management guidelines as indicated by Kubi. Or, if you are feeling lazy, use this:
NSString *outputString = [[NSString stringWithString:#"http://wap?keyword="] stringByAppendingString: UserText];
outputString = [outputString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
Or this:
NSString *outputString = [NSString stringWithFormat:#"http://wap?keyword=%#",UserText];
outputString = [outputString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
Don't pay any attention to your retain count. That number will fluctuate for reasons entirely out of your control and trying to determine why the number is what it is will drive you crazy (and not do you any good).
Follow proper Obj-C memory management guidelines, use the leaks detector in Instruments, and run the static analyzer when you build. If you do all that, you'll be fine.
No depending on your requirement and object allocation, your retain count will definitely increase. But the main thing you need to keep in mind that release all the allocated object at proper place and proper time.