iPhone: утечка памяти на автореализуемом объекте?

Я использую класс XMLParser, который содержит массив с объектами XMLElement. XMLElement выделяется с помощью операции автозапуска. Однако по какой-то причине я получаю утечку памяти (с помощью инструментов) в этой строке:

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

Я также освобождаю объект XMLParser в классе вызывающей стороны. В качестве комментария я выделил «проблемные» строки в исходном коде XMLParser ниже.

Я действительно все перепробовал, чтобы исправить, но, к сожалению, не понимаю, почему это вообще происходит. Любая помощь приветствуется!

Большое тебе спасибо!

// --- 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

person user480451    schedule 19.10.2010    source источник


Ответы (5)


Вы должны выпустить currentElement в своем dealloc методе XMLParser.

Он создается как autorelease, но затем назначается свойству retain, поэтому вы фактически сохраняете его один раз (что хорошо).

ОБНОВЛЕНИЕ: вы должны делать self.currentElement = nil;, когда закончите с этим, а не currentElement = nil;.

person jv42    schedule 19.10.2010

Этот:

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

сохраняет currentElement (потому что свойство объявлено с retain).

Позже, в parser:didEndElement:namespaceURI:qualifiedName:, вы сделаете это:

currentElement = nil;

что вызывает утечку, потому что вы не выпускаете currentElement.

person Ole Begemann    schedule 19.10.2010
comment
Спасибо за ответ. Я обновил его, как вы предложили, но у меня все еще возникает утечка памяти. Вот обновленный код: - person user480451; 19.10.2010
comment
- (void) parser: (NSXMLParser *) parser didEndElement: (NSString *) elementName namespaceURI: (NSString *) namespaceURI qualifiedName: (NSString *) qName {// NSLog (@Processing didEndElement:% @, elementName); если (currentElement == nil) {если (currentParentElement.parentElement) {self.currentParentElement = currentParentElement.parentElement; }} еще {currentElement.textValue = currentElementValue; [выпуск currentElementValue]; self.currentElementValue = ноль; self.currentParentElement = currentElement.parentElement; self.currentElement = ноль; }} - person user480451; 19.10.2010
comment
@ user480451: вы серьезно думаете, что кто-нибудь сможет это прочитать? Отредактируйте вопрос, чтобы вставить в него код. - person JeremyP; 19.10.2010
comment
:) да вы абсолютно правы. я не нашел здесь кнопки ответа ... поэтому добавлю ее в качестве ответа - person user480451; 19.10.2010

Судя по тому, что я вижу на этой странице, это задокументированная ошибка Apple. У меня возникла такая же проблема в некоторых моих приложениях ...

person gabaum10    schedule 19.10.2010

Ваш XMLParser запущен в фоновом потоке? Если это так, вам нужно создать NSAutoReleasePool для этого потока, чтобы ваш [[[XMLElement alloc] init] autorelease] вызов работал.

person highlycaffeinated    schedule 19.10.2010

Привет, ребята. Причина всего в одной простой вещи,

вместо

@property(nonatomic, retain) XMLElement *parentElement;

должно быть

@property(nonatomic, assign) XMLElement *parentElement;

также вам нужно удалить [parentElement release] из dealloc

причина в том, что вы создаете циклические ссылки. родитель для ребенка и детей для родителей

и когда вы реализуете объект парсера, элементы все еще появляются в памяти с указателем xount> 0

person nuinisk    schedule 28.10.2010