user480451
user480451

Reputation: 41

iPhone: memory leak on autoreleased object?

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

Upvotes: 3

Views: 1430

Answers (5)

nuinisk
nuinisk

Reputation: 1

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

Upvotes: 0

jv42
jv42

Reputation: 8593

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

Upvotes: 3

highlycaffeinated
highlycaffeinated

Reputation: 19867

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.

Upvotes: 0

gabaum10
gabaum10

Reputation: 3827

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

Upvotes: 0

Ole Begemann
Ole Begemann

Reputation: 135588

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.

Upvotes: 2

Related Questions