NSXMLParser & memory leaks

Posted by HBR on Stack Overflow See other posts from Stack Overflow or by HBR
Published on 2010-04-17T11:06:36Z Indexed on 2010/04/17 11:13 UTC
Read the original article Hit count: 445

Hi, I am parsing an XML file using a custom class that instanciates & uses NSXMLParser. On the first call everything is fine but on the second, third and later calls Instruments show tens of memory leaks on certain lines inside didEndElement, didEndElement and foundCharacters functions. I googled it and found some people having this issue, but I didn't find anything that could really help me.

My Parser class looks like this :

Parser.h

@interface XMLParser : NSObject  {
NSMutableArray *data;
NSMutableString *currentValue;
NSArray *xml;
NSMutableArray *videos;
NSMutableArray *photos;
NSXMLParser *parser;
NSURLConnection *feedConnection;
NSMutableData *downloadedData;

Content *content;
Video *video;

BOOL nowPhoto;
BOOL nowVideo;
    BOOL finished;
    BOOL webTV;
}


-(void)parseXML:(NSURL*)xmlURL;
-(int)getCount;
-(NSArray*)getData;
//- (void)handleError:(NSError *)error;

//@property(nonatomic, retain) NSMutableString *currentValue;
@property(nonatomic, retain) NSURLConnection *feedConnection;
@property(nonatomic, retain) NSMutableData *downloadedData;
@property(nonatomic, retain) NSArray *xml;
@property(nonatomic, retain) NSXMLParser *parser;
@property(nonatomic, retain) NSMutableArray *data;
@property(nonatomic, retain) NSMutableArray *photos;
@property(nonatomic, retain) NSMutableArray *videos;
@property(nonatomic, retain) Content *content;
@property(nonatomic, retain) Video *video;

@property(nonatomic) BOOL finished;
@property(nonatomic) BOOL nowPhoto;
@property(nonatomic) BOOL nowVideo;
@property(nonatomic) BOOL webTV;
@end

Parser.m

#import "Content.h"
#import "Video.h"
#import "Parser.h"
#import <CFNetwork/CFNetwork.h>

@implementation XMLParser

@synthesize xml, parser, finished, nowPhoto, nowVideo, webTV;
@synthesize feedConnection, downloadedData, data, content, photos, videos, video;

-(void)parseXML:(NSURL*)xmlURL {

    /*
    NSURLRequest *req = [NSURLRequest requestWithURL:xmlURL];
    self.feedConnection = [[[NSURLConnection alloc] initWithRequest:req delegate:self] autorelease];
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    */

    [[NSURLCache sharedURLCache] setMemoryCapacity:0];
    [[NSURLCache sharedURLCache] setDiskCapacity:0];


    NSXMLParser *feedParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
    //NSXMLParser *feedParser = [[NSXMLParser alloc] initWithData:theXML];
    [self setParser:feedParser];
    [feedParser release];

    [[self parser] setDelegate:self];
    [[self parser] setShouldResolveExternalEntities:YES];
    [[self parser] parse];

}


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

    if ([elementName isEqualToString:@"articles"]) {
        self.finished = NO;
        self.nowPhoto = NO;
        self.nowVideo = NO;
        self.webTV = NO;

        if (!data) {
            NSMutableArray *tmp = [[NSMutableArray alloc] init];
            [self setData:tmp];
            [tmp release];
            return ;
        }
    }

    if ([elementName isEqualToString:@"WebTV"]) {
        self.finished = NO;
        self.nowPhoto = NO;
        self.nowVideo = NO;
        self.webTV = YES;

        if (!data) {
            NSMutableArray *tmp = [[NSMutableArray alloc] init];
            [self setData:tmp];
            [tmp release];
            return ;
        }
    }


    if ([elementName isEqualToString:@"photos"]) {
        if (!photos) {
            NSMutableArray *tmp = [[NSMutableArray alloc] init];
            [self setPhotos:tmp];
            [tmp release];
            return;
        }
    }

    if ([elementName isEqualToString:@"videos"]) {
        if (!videos) {
            NSMutableArray *tmp = [[NSMutableArray alloc] init];
            [self setVideos:tmp];
            [tmp release];
            return;
        }
    }


    if ([elementName isEqualToString:@"photo"]) {
        self.nowPhoto = YES;
        self.nowVideo = NO;
    }

    if ([elementName isEqualToString:@"video"]) {
        self.nowPhoto = NO;
        self.nowVideo = YES;
    }

    if ([elementName isEqualToString:@"WebTVItem"]) {
        if (!video) {
            Video *tmp = [[Video alloc] init];
            [self setVideo:tmp];
            [tmp release];
        }

        NSString *videoId = [attributeDict objectForKey:@"id"];
        [[self video] setVideoId:[videoId intValue]];

    }

    if ([elementName isEqualToString:@"article"]) {
        if (!content) {
            Content *tmp = [[Content alloc] init];
            [self setContent:tmp];
            [tmp release];
        }

        NSString *contentId = [attributeDict objectForKey:@"id"];
        [[self content] setContentId:[contentId intValue]];

        return;
    }


    if ([elementName isEqualToString:@"category"]) {
        NSString *categoryId = [attributeDict objectForKey:@"id"];
        NSString *parentId = [attributeDict objectForKey:@"parent"];

        [[self content] setCategoryId:[categoryId intValue]];
        [[self content] setParentId:[parentId intValue]];


        categoryId = nil;
        parentId = nil;

        return;
    }

    if ([elementName isEqualToString:@"vCategory"]) {
        NSString *categoryId = [attributeDict objectForKey:@"id"];
        NSString *parentId = [attributeDict objectForKey:@"parent"];

        [[self video] setCategoryId:[categoryId intValue]];
        [[self video] setParentId:[parentId intValue]];

        categoryId = nil;
        parentId = nil;


        return;
    }


}


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


    if (!currentValue) {
        currentValue = [[NSMutableString alloc] initWithCapacity:1000];
    }


    if (currentValue != @"\n")
    [currentValue appendString:string];             
}




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

    NSString *cleanValue = [currentValue stringByReplacingOccurrencesOfString:@"\n" withString:@""]   ;

    if ([elementName isEqualToString:@"articles"]) {
        self.finished = YES;

        //[content release];
    }

    if ([elementName isEqualToString:@"article"]) {
        [[self data] addObject:[self content]];
        [self setContent:nil];
        [self setPhotos:nil];
        [self setVideos:nil];

        /*
        [content release];
        content = nil;

        [videos release];
        videos = nil;

        [photos release];
        photos = nil;
         */
    }

    if ([elementName isEqualToString:@"WebTVItem"]) {
        [[self data] addObject:[self video]];
        [self setVideo:nil];

        //[video release];
        //video = nil;
    }

    if ([elementName isEqualToString:@"title"]) {
        //NSLog(@"Tit: %@",cleanValue);
        [[self content] setTitle:cleanValue];
    }

    if ([elementName isEqualToString:@"vTitle"]) {
        [[self video] setTitle:cleanValue];
    }

    if ([elementName isEqualToString:@"link"]) {
        //NSURL *url = [[NSURL alloc] initWithString:cleanValue] ;
        [[self content] setUrl:[NSURL URLWithString:cleanValue]];
        [[self content] setLink: cleanValue];

        //[url release];
        //url = nil;
    }

    if ([elementName isEqualToString:@"vLink"]) {
        [[self video] setLink:cleanValue];
        [[self video] setUrl:[NSURL URLWithString:cleanValue]];
    }


    if ([elementName isEqualToString:@"teaser"]) {
        NSString *tmp = [cleanValue stringByReplacingOccurrencesOfString:@"##BREAK##" withString:@"\n"];
        [[self content] setTeaser:tmp];
        tmp = nil;
    }

    if ([elementName isEqualToString:@"content"]) {
        NSString *tmp = [cleanValue stringByReplacingOccurrencesOfString:@"##BREAK##" withString:@"\n"];
        [[self content] setContent:tmp];

        tmp = nil;
    }

    if ([elementName isEqualToString:@"category"]) {
        [[self content] setCategory:cleanValue];
    }

    if ([elementName isEqualToString:@"vCategory"]) {
        [[self video]   setCategory:cleanValue];
    }


    if ([elementName isEqualToString:@"date"]) {
        [[self content] setDate:cleanValue];
    }

    if ([elementName isEqualToString:@"vDate"]) {
        [[self video] setDate:cleanValue];
    }

    if ([elementName isEqualToString:@"thumbnail"]) {
        [[self content] setThumbnail:[NSURL URLWithString:cleanValue]];

        [[self content] setThumbnailURL:cleanValue];
    }

    if ([elementName isEqualToString:@"vThumbnail"]) {
        [[self video] setThumbnailURL:cleanValue];
        [[self video] setThumbnail:[NSURL URLWithString:cleanValue]];
    }

    if ([elementName isEqualToString:@"vDirectLink"]){
        [[self video] setDirectLink: cleanValue];
    }

    if ([elementName isEqualToString:@"preview"]){
        [[self video] setPreview: cleanValue];
    }

    if ([elementName isEqualToString:@"thumbnail_position"]){
        [[self content] setThumbnailPosition: cleanValue];
    }


    if ([elementName isEqualToString:@"url"]) {
        if (self.nowPhoto == YES) {
            [[self photos] addObject:cleanValue];
        }
        else if (self.nowVideo == YES) {
            [[self videos] addObject:cleanValue];
        }
    }


    if ([elementName isEqualToString:@"photos"]) {

        [[self content] setPhotos:[self photos]];
        //[photos release];
        //photos    = nil;

         self.nowPhoto = NO;
    }


    if ([elementName isEqualToString:@"videos"]) {
        [[self content] setVideos:[self videos]];
        //[videos release];
        //videos    = nil;

        self.nowVideo = NO;
    }

    //[cleanValue release];
    //cleanValue = nil;

    [currentValue release];
    currentValue = nil;

}

- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"Error" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; 
    [alert show]; 
    [alert release];
}

-(NSArray*)getData {


    return data;
}


-(int)getCount {
    return [data count];
}

- (void)dealloc {
    [parser release];
    //[data release];
    //[photos release];
    //[videos release];
    //[video release];
    //[content release];
    [currentValue release];

    [super dealloc];
}


@end

Somewhere in my code, I create an instance of this class :

XMLParser* feed = [[XMLParser alloc] init];
[self setRssParser:feed];
[feed release];

// Parse feed
NSString *url = [NSString stringWithFormat:@"MyXMLURL"]; 
[[self rssParser] parseXML:[NSURL URLWithString:url]];

Now the problem is that after the first (which has zero leaks), instruments shows leaks in too many parts like this one (they are too much to enumerate them all, but all the calls look the same, I made the leaking line bold) :

in didEndElement :

if ([elementName isEqualToString:@"link"]) {
    // THIS LINE IS LEAKING => INSTRUMENTS SAYS IT IS A NSCFString LEAK 
    [self content] setUrl:[NSURL URLWithString:cleanValue]];
    [[self content] setLink: cleanValue];

}

Any idea how to fix this pealse ? Could this be the same problem as the one mentioned (as an apple bug) by Lee Amtrong here :http://stackoverflow.com/questions/1598928/nsxmlparser-leaking

© Stack Overflow or respective owner

Related posts about iphone-sdk

Related posts about nsxmlparser