views:

760

answers:

1

I'm suffering from a bad memory leak parsing one of my xml documents.

I'm using the NSXMLParser to iterate each node (album in the xml sample below), then iterate each photo node and add the result to an NSArray.

I have 2 retained properties, which store two values on each loop. These values are added to a NSMutableDictionary object along with another NSArray containing each photo node. The resulting dictionary is added to an NSArray to be used elsewhere in the application.

I have a button which reloads the xml document at certain points in the application. The next time it's called is when the leaks are occurring. Instruments is showing NSCFArray and NSCFString are leaking and it's showing up in didEndElement.

Where on earth am I going wrong. Any advice would be helpful.

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:@"photo"]){
    self.strPhotoPath = [attributeDict objectForKey:@"iphone"];
}else if ([elementName isEqualToString:@"album"]) {

    dicItem = [[[NSMutableDictionary alloc] init] autorelease];

    self.strCurrentTitle = [attributeDict objectForKey:@"band_name"];
    self.strCurrentLocation = [attributeDict objectForKey:@"location"];

}
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{   
if([elementName isEqualToString:@"photo"]){
    [self.arrPhotos addObject:self.strPhotoPath];
}else if ([elementName isEqualToString:@"album"]) {
    [dicItem setObject:self.strCurrentTitle forKey:@"album"];
    [dicItem setObject:self.strCurrentLocation forKey:@"location"];
    [dicItem setObject:[self.arrPhotos copy] forKey:@"photos"];

    [self.arrAlbums addObject:dicItem];

    [self.arrPhotos removeAllObjects];
}
}

Here's a snip of the xml.

<albums type='array'>
<album location='Album 1' date='2009-12-04 22:47:48 UTC' album_name='' band_name='Band 1'>
<photo display_on_website='true' standard='/system/photos/3396/original/Photo1-DSC8894.jpg' thumb='/system/photos/3396/thumb/Photo1-DSC8894.jpg' date='2009-12-04 22:47:48' exif_data='NIKON D300, F:2.8, Shutter:1/80, Focal:15mm, ISO:1600' iphone='/system/photos/3396/iPhone/Photo2-DSC8894.jpg' available_for_print='false'/>
<photo display_on_website='true' standard='/system/photos/3403/original/Photo2-DSC9146.jpg' thumb='/system/photos/3403/thumb/Photo3-DSC9146.jpg' date='2009-12-04 23:19:27' exif_data='NIKON D300, F:4.5, Shutter:1/160, Focal:70mm, ISO:1600' iphone='/system/photos/3403/iPhone/Photo3-DSC9146.jpg' available_for_print='false'/>
</album>
<album location='Album 2' date='2009-12-04 22:47:48 UTC' album_name='' band_name='Band 2'>
<photo display_on_website='true' standard='/system/photos/3396/original/Photo3-DSC8894.jpg' thumb='/system/photos/3396/thumb/Photo3-DSC8894.jpg' date='2009-12-04 22:47:48' exif_data='NIKON D300, F:2.8, Shutter:1/80, Focal:15mm, ISO:1600' iphone='/system/photos/3396/iPhone/Photo3-DSC8894.jpg' available_for_print='false'/>
<photo display_on_website='true' standard='/system/photos/3403/original/Photo4-DSC9146.jpg' thumb='/system/photos/3403/thumb/Photo4-DSC9146.jpg' date='2009-12-04 23:19:27' exif_data='NIKON D300, F:4.5, Shutter:1/160, Focal:70mm, ISO:1600' iphone='/system/photos/3403/iPhone/Photo4-DSC9146.jpg' available_for_print='false'/>
<photo display_on_website='true' standard='/system/photos/3403/original/Photo5-DSC9146.jpg' thumb='/system/photos/3403/thumb/Photo5-DSC9146.jpg' date='2009-12-04 23:19:27' exif_data='NIKON D300, F:4.5, Shutter:1/160, Focal:70mm, ISO:1600' iphone='/system/photos/3403/iPhone/Photo5-DSC9146.jpg' available_for_print='false'/>
</album>
</albums>
+1  A: 
[dicItem setObject:[self.arrPhotos copy] forKey:@"photos"];

I would guess this line is a culprit. The copy is never autoreleased or released, just replaced each time the parser encounters a album element. Use:

[dicItem setObject:[[self.arrPhotos copy] autorelease] forKey:@"photos"];

Since you are creating a copy of the array, all of the objects in the array also get their retain count increased, so if the array becomes unreachable (by overwriting it in the dictionary), all the strings that the array contained will not be released properly, and neither will the array.

dreamlax
That's done it. Now that you've pointed it out it's completely obvious. I've been sat here for 3 hours fixing memory leaks throughout, what is quite a big app, and I need a second pair of eyes. Thanks again.
Lee M