views:

89

answers:

6

Dear all,

I am using the XMLParser class, which contains an array with XMLElement objects. The XMLElement is being allocated using the autorelease operation. However, for some reason I'm getting a memory leak (using instruments) on this line:

self.currentElement = [[[XMLElement alloc] init] autorelease];

I'm also releasing the XMLParser object in the caller class. I've highlighted the "problematic" lines in the XMLParser source code below as comment.

I really tried everything to fix it, but unfortunately I do not understand why this is happening at all. Any help is appreciated!

Thank you very much!

// --- XMLElement

#import <Foundation/Foundation.h>


@interface XMLElement : NSObject {
     NSDictionary *attributes;
     NSMutableArray *children;
     NSString *textValue;
     NSString *tagName;
     XMLElement *parentElement;
}

-(id) init;
-(void) addChild:(XMLElement*) child;

@property(nonatomic, retain) NSDictionary *attributes;
@property(nonatomic, retain) NSMutableArray *children;
@property(nonatomic, retain) NSString *textValue;
@property(nonatomic, retain) NSString *tagName;
@property(nonatomic, retain) XMLElement *parentElement;

@end


#import "XMLElement.h"


@implementation XMLElement

@synthesize attributes, children, textValue, parentElement, tagName;

-(id)init {
     if(self = [super init]) {
          children = [[NSMutableArray alloc] init];
     }
     return self;
}

-(void) addChild:(XMLElement*) child{
     [self.children addObject:child];
}

- (void)dealloc {
     [attributes release];
     [children release];
     [textValue release];
     [tagName release];
     [parentElement release];
    [super dealloc];
}

@end





// --- XMLParser

#import <Foundation/Foundation.h>
#import "XMLElement.h"

@interface XMLParser : NSObject<NSXMLParserDelegate> {
     XMLElement *currentElement;
     XMLElement *currentParentElement;
     NSMutableString *currentElementValue;
}

- (BOOL)parseData: (NSData*) dataToParse;

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
  namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
     attributes:(NSDictionary *)attributeDict;

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
  namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;

@property (nonatomic, retain) XMLElement *currentParentElement;
@property (nonatomic, retain) XMLElement *currentElement;
@property (nonatomic, retain) NSMutableString *currentElementValue;

@end


#import "XMLParser.h"


@implementation XMLParser

@synthesize currentElementValue, currentElement, currentParentElement;

- (BOOL)parseData: (NSData*) dataToParse {

     NSXMLParser *parser = [[NSXMLParser alloc] initWithData:dataToParse];
     [parser setDelegate:self];
     BOOL success = [parser parse];
     [parser release];
     return success;
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
  namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
     attributes:(NSDictionary *)attributeDict{
     if(currentElement){
          self.currentParentElement = currentElement;
     }


     // ------------------------------------------------------------
     // Instruments is marking this line as source of the leak with 90%
     self.currentElement = [[[XMLElement alloc] init] autorelease];
     // --------

     currentElement.tagName = elementName;
     currentElement.attributes = attributeDict;
     currentElement.parentElement = self.currentParentElement;
     if(self.currentParentElement){
          [self.currentParentElement addChild:currentElement]; // and this one with 10%
     }

     self.currentElementValue = nil;
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
     if(!currentElement) {
          return;
     }

     if(currentElementValue == nil)
          self.currentElementValue = [NSMutableString stringWithString:string];
     else
          [currentElementValue appendString:string];
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
  namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
     if(currentElement == nil){
          if( currentParentElement.parentElement){
               self.currentParentElement = currentParentElement.parentElement;
          }
     }else{
          currentElement.textValue = currentElementValue; 
          [currentElementValue release];
          currentElementValue = nil;
          self.currentParentElement = currentElement.parentElement;
          currentElement = nil;
     }
}

- (void)dealloc {
     [currentParentElement release];
     [currentElement release];
    [super dealloc];
}

@end
+2  A: 

You must release currentElement in your dealloc method in XMLParser.

It's created as autorelease but then assigned to a retain property, so you are actually retaining it once (which is good).

UPDATE: you should do self.currentElement = nil; when you are done with it, not currentElement = nil;.

jv42
+1  A: 

This:

self.currentElement = [[[XMLElement alloc] init] autorelease];

retains currentElement (because the property is declared with retain).

Later, in parser:didEndElement:namespaceURI:qualifiedName:, you do this:

currentElement = nil;

which causes the leak because you are not releasing currentElement.

Ole Begemann
Thanks for the answer. I've updated it like you suggested, but I still get the memory leak. Here is the updated code:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{ //NSLog(@"Processing didEndElement: %@", elementName); if(currentElement == nil){ if( currentParentElement.parentElement){ self.currentParentElement = currentParentElement.parentElement; } }else{ currentElement.textValue = currentElementValue; [currentElementValue release]; self.currentElementValue = nil; self.currentParentElement = currentElement.parentElement; self.currentElement = nil; }}
@user480451: do you seriously expect anybody to be able to read that? Edit the question to put the code in it.
JeremyP
:) yea you are absolutely right. i didn't find the "reply" button here... so i'll add it as an answer
A: 
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName

namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{

if(currentElement == nil){

if( currentParentElement.parentElement){

self.currentParentElement = currentParentElement.parentElement;

}

} else {

currentElement.textValue = currentElementValue;

[currentElementValue release];

self.currentElementValue = nil;

self.currentParentElement = currentElement.parentElement;

self.currentElement = nil;

}

So in a nutshell: I've added the self. but i'm still having the same memory leak problem. }

A: 

From what I see on this page, this is a documented apple bug. I have experienced the same problem in some of my apps...

Geoff Baum
A: 

Is your XMLParser being run on a background thread? If so, you need to create an NSAutoReleasePool for that thread in order for your [[[XMLElement alloc] init] autorelease] call to work.

highlycaffeinated
A: 

HI guys. the reason is just one simple thing,

instead of

@property(nonatomic, retain) XMLElement *parentElement;

must be

@property(nonatomic, assign) XMLElement *parentElement;

also you need delete [parentElement release] from dealloc

reason is that you are creating cyclic referances. parent to child and chils to parent

and when you are realesing parser object the elements still ocurrs in memory with pointer xount > 0

nuinisk