views:

988

answers:

4

Update: I edited the code, but the problem persists...

Hi everyone,
this is my first post here - I found this place a great ressource for solving many of my questions. Normally I try my best to fix anything on my own but this time I really have no idea what goes wrong, so I hope someone can help me out.
I am building an iPhone app that parses a couple of xml files using TouchXML. I have a class XMLParser, which takes care of downloading and parsing the results. I am getting memory leaks when I parse an xml file more than once with the same instance of XMLParser. Here is one of the parsing snippets (just the relevant part):

for(int counter = 0; counter < [item childCount]; counter++) {  
  CXMLNode *child = [item childAtIndex:counter];
  if([[child name] isEqualToString:@"PRODUCT"]) 
  {
   NSMutableDictionary *product = [[NSMutableDictionary alloc] init];
   for(int j = 0; j < [child childCount]; j++) {
    CXMLNode *grandchild = [child childAtIndex:j];
    if([[grandchild stringValue] length] > 1) {
     NSString *trimmedString = [[grandchild stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
     [product setObject:trimmedString forKey:[grandchild name]];
    }
   }

   // Add product to current category array
   switch (categoryId) {
    case 0:
     [self.mobil addObject: product];
     break;
    case 1:
     [self.allgemein addObject: product];
     break;
    case 2:
     [self.besitzeIch addObject: product];
     break;
    case 3:
     [self.willIch addObject: product];
     break;
    default:
     break;
   }
   [product release];
  }

 }

The first time, I parse the xml no leak shows up in instruments, the next time I do so, I got a lot of leaks (NSCFString / NSCFDictionary).
Instruments points me to this part inside CXMLNode.m, when I dig into a leaked object:

theStringValue = [NSString stringWithUTF8String:(const char *)theXMLString];
if ( _node->type != CXMLTextKind )
   xmlFree(theXMLString);
}

return(theStringValue);

I really spent a long time and tried multiple approaches to fix this, but to no avail so far, maybe I am missing something essential?

Any help is highly appreciated, thank you!

+2  A: 

The issue is likely in this line:

[self.mobil addObject:[product copy]];

By calling for a copy of product you're creating a new NSMutableDictionary instance with a retain count of 1. The mobil instance, however, will increment the copy's retain count when you send it the addObject: message, so the retain count of the copy is now 2. Generally speaking, an object is responsible for handling its own object memory, so any time you message setFoo: or addObject:, you can just pass the object directly, even if its autoreleased or you are planning to release it right after the call; it is the receiver's responsibility to retain the object you're passing if it needs to hold onto it.

Because you did not assign the copy to any variable, you do not have a pointer that you can use to decrement the copy's retain count now that you're no longer interested in it, so even if mobil releases the product copy at some point, the copy will never reach a retain count of 0. Your [product release] statement at the end of the for loop releases the original product object, not the copy you created.

Instead, try the following and see if instruments is happier:

[self.mobil addObject:product];
Jarret Hardie
Thank you very much - that was silly of me. Unfortunately the leaks still exist, but now inside the CXMLNode.m class at this line: theStringValue = [NSString stringWithUTF8String:(const char *)theXMLString]; if ( _node->type != CXMLTextKind ) xmlFree(theXMLString); } return(theStringValue);
Chris
ah sorry, I could not get the formatting right...
Chris
Hmmm... that looks like more of a warning of a possible leak, rather than a guaranteed leek. That said, I went browsing through the source for CXMLNode in TouchXML and I can't find that line at all... which version of the project do you have, and which line is it on? I note from the TouchCode google user group that the author, Jonathan Wight, is pretty good about responding to posts re: memory leaks: http://groups.google.com/group/touchcode-dev
Jarret Hardie
I found the leak in my own code, so there is nothing wrong inside CXMLNode, it was just an entry in the Instruments stack that showed up, but it was not the leaking spot...
Chris
+1  A: 

In simple terms, every time you use copy, you also have to use release/autorelease somewhere.

And in this instance, the even easier answer is to not use copy in the first place, since you aren't doing anything with the original version of product after you've copied it.

grahamparks
A: 

I fixed the problem myself. It was kind of stupid, but maybe someone might come across the same thing, so I am going to post it here.

1) I had mutable array set up as instance variables like this:

@interface XMLParser : NSObject {

// ...  
NSMutableArray *mobil;  
// ...  
}   
@property(nonatomic, retain) NSMutableArray *mobil;  
@end

Everytime I wanted to restore new data inside I did:
self.mobil = nil;
Which did not what I wanted to do, so this is the better approach:
[self.mobil removeAllObjects];

2) The dealloc method has to be like this to fix the leaks (because mobil is defined as a property):
-(void)dealloc {
[mobil release];
self.mobil = nil;
}

Whew, that has been a lot of work to find out - hope it saves someone else some time :-)

Chris
A: 

I am having the same problem and I try your approach. I still have the memory leak warning. where do you remove your self.mobil objects?

thank you,

wannamobile
I remove the objects everytime, I want to add new data to it. Maybe you could show me your code (parts), so I might help finding the problem.
Chris