Question

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!

Was it helpful?

Solution

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];

OTHER TIPS

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.

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 :-)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top