Memory Leak with NSMutableString appendString
I am using an XMLParser to parse some XML data, which uses an NSMutableString *resultString to store the tag characters. At every (- parser: didStarElement...) method I allocate and init the resultString-ivar.
- (void)parser: (NSXMLParser *)parser didStartElement: (NSString *)elementName namespaceURI: (NSString *)namespaceURI qualifiedName: (NSString *)qName attributes: (NSDictionary *)attributeDict {
// Alot of if-statements to sort subtags
// /.../
resultString = [[NSMutableString alloc] init];
recordResults = YES;
}
The string is appended in the parser:foundCharacters-method. I read somewhere that autoreleased objects, like the string inside appendString could cause the image of a memory leak. So i added a local autorelease pool to make sure it got drained right away (no change in behavior though):
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
NSAutoreleasePool *pool = [[N开发者_开发技巧SAutoreleasePool alloc] init];
if(recordResults) {
[resultString appendString: string];
}
[pool drain];
}
In the parser:didEndElement... I finally release and nil out the resultsString:
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
// Alot of if statements to handle differnt tags
// each of which has the structure of the last else-statement
// In other words, I am pretty sure I've covered every possible
// case to prevent the resultString from
// not getting released and niled out
if(...) {
...
}
else if(...) {
...
}
else {
if(resultString != nil) {
[dataDict setObject: resultString forKey: elementName];
[resultString release];
resultString = nil;
}
}
Instruments Leak-tool flags the parser:foundCharacter-method as a source for memory leakage, so I wonder if this is caused by appendString. Or if you can find something in this code that is way out wrong. This is a rather memory craving application, parsing quite a few and sometimes moderately big XML-files on an iPhone, so my question would be how to find a work around, if the NSMutableString appendString is not appropriate in this case...
Thanks in advance!
If an end tag is missing, you will have a memory leak. It is better to have any allocations in parserDidStartDocument: and deallocations in parserDidEndDocument:, as these are guaranteed to be paired. And instead of allocating resultString in didStartElement, you just truncate it there.
I don't think you can know if appendString
is using autorelease
or not because the method changes it's object rather than returning a new object. In other words, because it's changing itself you're not responsible for it's memory management. You also don't own string
so you don't have to release or autorelease it.
Check this out: http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-BAJHFBGH
That will probably clarify things, but the key thing with autorelease is that "new" objects returned by something other than alloc/init areautoreleased. In this case no new object is returned by appendString, and string could simply be retained, passed into the parser callback, then released by the caller later, so who knows if that's autoreleased.
The time when you have to autorelease something is when you have a function/method that uses alloc/new/copy, then returns the object with no way for the function itself to release it in the future.
The auto-release pool is doing nothing, since you are not auto-releasing any objects - you are allocing the mutable string in one location, releasing it in another. "appendString" does nothing at all to the retain count of a MutableString.
As for the leak, here is the deal - Leaks is telling you where memory leaking was allocated. So that means that everything is OK in the part that allocates the memory - what is not OK, is that somewhere else the memory that was retained in that method was supposed to be released and it was not.
So at some point later, it would seem you get out resultString from the dataDict you place it in, retain it and do not release it (it seems to me like you release it OK in parsing so that would not be the culprit). To verify this is so, change the insert of the string to be::
[dataDict setObject:[[resultString copy] autorelease] forKey: elementName];
And leaks should tell you the leak is there. To help track down the leak, any time you extract a string from that dataDict, you can [copy] it and then you will get closer to the exact code that is leaking strings.
Basically Leaks is like the opening of a mystery book with someone dead. It's up to you to figure out who the killer is - or in the case of leaks, who was supposed to do the killing but didn't get around to it.
精彩评论