05-19-2010, 08:26 AM
#1 (permalink )
Registered Member
Join Date: May 2010
Posts: 35
[SOLVED] NSXMLParser NSCFString memory leak
I am building an app that parses an rss feed. In the app there are two different types of feeds with different names for the elements in the feed, so I have created an NSXMLParser NSObject that takes the name of the elements of each feed before parsing. Here is my code:
NewsFeedParser.h
Code:
#import <Foundation/Foundation.h>
@interface NewsFeedParser : NSObject {
NSInteger NewsSelectedCategory;
NSXMLParser *NSXMLNewsParser;
NSMutableArray *newsCategories;
NSMutableDictionary *NewsItem;
NSMutableString *NewsCurrentElement, *NewsCurrentElement1, *NewsCurrentElement2, *NewsCurrentElement3;
NSString *NewsItemType, *NewsElement1, *NewsElement2, *NewsElement3;
NSInteger NewsNumElements;
}
- (void) parseXMLFileAtURL:(NSString *)URL;
@property(nonatomic, retain) NSString *NewsItemType;
@property(nonatomic, retain) NSString *NewsElement1;
@property(nonatomic, retain) NSString *NewsElement2;
@property(nonatomic, retain) NSString *NewsElement3;
@property(nonatomic, retain) NSMutableArray *newsCategories;
@property(assign, nonatomic) NSInteger NewsNumElements;
@end
NewsFeedParser.m
Code:
#import "NewsFeedParser.h"
@implementation NewsFeedParser
@synthesize NewsItemType;
@synthesize NewsElement1;
@synthesize NewsElement2;
@synthesize NewsElement3;
@synthesize newsCategories;
@synthesize NewsNumElements;
- (void)parserDidStartDocument:(NSXMLParser *)parser{
}
- (void)parseXMLFileAtURL:(NSString *)URL
{
newsCategories = [[NSMutableArray alloc] init];
URL = [URL stringByReplacingOccurrencesOfString:@" " withString:@""];
URL = [URL stringByReplacingOccurrencesOfString:@"\n" withString:@""];
URL = [URL stringByReplacingOccurrencesOfString:@" " withString:@""];
//you must then convert the path to a proper NSURL or it won't work
NSURL *xmlURL = [NSURL URLWithString:URL];
// here, for some reason you have to use NSClassFromString when trying to alloc NSXMLParser, otherwise you will get an object not found error
// this may be necessary only for the toolchain
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
NSXMLNewsParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[NSXMLNewsParser setDelegate:self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[NSXMLNewsParser setShouldProcessNamespaces:NO];
[NSXMLNewsParser setShouldReportNamespacePrefixes:NO];
[NSXMLNewsParser setShouldResolveExternalEntities:NO];
[NSXMLNewsParser parse];
[NSXMLNewsParser release];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSString * errorString = [NSString stringWithFormat:@"Unable to download story feed from web site (Error code %i )", [parseError code]];
NSLog(@"error parsing XML: %@", errorString);
UIAlertView * errorAlert = [[UIAlertView alloc] initWithTitle:@"Error loading content" message:errorString delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
[errorAlert show];
[errorAlert release];
[errorString release];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
NewsCurrentElement = [elementName copy];
if ([elementName isEqualToString:NewsItemType])
{
// clear out our story item caches...
NewsItem = [[NSMutableDictionary alloc] init];
NewsCurrentElement1 = [[NSMutableString alloc] init];
NewsCurrentElement2 = [[NSMutableString alloc] init];
if(NewsNumElements == 3)
{
NewsCurrentElement3 = [[NSMutableString alloc] init];
}
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:NewsItemType])
{
// save values to an item, then store that item into the array...
[NewsItem setObject:NewsCurrentElement1 forKey:NewsElement1];
[NewsItem setObject:NewsCurrentElement2 forKey:NewsElement2];
if(NewsNumElements == 3)
{
[NewsItem setObject:NewsCurrentElement3 forKey:NewsElement3];
}
[newsCategories addObject:[[NewsItem copy] autorelease]];
[NewsCurrentElement release];
[NewsCurrentElement1 release];
[NewsCurrentElement2 release];
if(NewsNumElements == 3)
{
[NewsCurrentElement3 release];
}
[NewsItem release];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
//NSLog(@"found characters: %@", string);
// save the characters for the current item...
if ([NewsCurrentElement isEqualToString:NewsElement1]) {
[NewsCurrentElement1 appendString:string];
} else if ([NewsCurrentElement isEqualToString:NewsElement2]) {
[NewsCurrentElement2 appendString:string];
} else if (NewsNumElements == 3 && [NewsCurrentElement isEqualToString:NewsElement3])
{
[NewsCurrentElement3 appendString:string];
}
}
- (void)dealloc {
[super dealloc];
[newsCategories release];
[NewsItemType release];
[NewsElement1 release];
[NewsElement2 release];
[NewsElement3 release];
}
When I create an instance of the class I do like so:
Code:
NewsFeedParser *categoriesParser = [[NewsFeedParser alloc] init];
if(newsCat == 0)
{
categoriesParser.NewsItemType = @"article";
categoriesParser.NewsElement1 = @"category";
categoriesParser.NewsElement2 = @"catid";
}
else
{
categoriesParser.NewsItemType = @"article";
categoriesParser.NewsElement1 = @"category";
categoriesParser.NewsElement2 = @"feedUrl";
}
[categoriesParser parseXMLFileAtURL:feedUrl];
newsCategories = [[NSMutableArray alloc] initWithArray:categoriesParser.newsCategories copyItems:YES];
[self.tableView reloadData];
[categoriesParser release];
If I run the app with the leaks instrument, the leaks point to the [NSXMLNewsParser parse] call in the NewsFeedParser.m.
Here is a screen shot of the Leaks instrument with the NSCFStrings leaking:
For the life of me I can't figure out where these leaks are coming from. Any help would be greatly appreciated.
Last edited by atticusalien; 05-21-2010 at 07:57 PM .
05-20-2010, 03:57 PM
#2 (permalink )
Registered Member
Join Date: May 2010
Posts: 35
bump*
05-20-2010, 04:54 PM
#3 (permalink )
Emphasizing Fundamentals
Join Date: Jul 2009
Location: NoVA / DC Area
Age: 36
Posts: 7,990
Rewrite it to use @properties for everything, then see where you are.
05-20-2010, 06:13 PM
#4 (permalink )
Registered Member
Join Date: May 2010
Posts: 35
Quote:
Rewrite it to use @properties for everything, then see where you are.
I'm new to Objective-C and Iphone development, so what would that do exactly? I thought you only needed to set a property for something that needs a getter and setter.
05-20-2010, 08:37 PM
#5 (permalink )
Registered Member
Join Date: Aug 2009
Location: Tasmania, Australia
Posts: 195
Quote:
Originally Posted by
atticusalien
I'm new to Objective-C and Iphone development, so what would that do exactly? I thought you only needed to set a property for something that needs a getter and setter.
You don't NEED to use properties, but it can simplify memory management. Ie, if you use a property correctly, it will ALWAYS release the old value when it sets a new value and never leak the old value.
With all the callbacks used in NSXMLParsers, it can be a bit tricky to follow what is going on, and which order things are being called in. It can be simpler to use properties in any situation where tracking retains and releases would otherwise be a bit difficult, even if the properties don't need to be accessed by any other objects.
05-20-2010, 08:42 PM
#6 (permalink )
Registered Member
Join Date: Aug 2009
Location: Tasmania, Australia
Posts: 195
Looks like "newsCategories" is defined as a property, but you're assigning to it using:
Code:
newsCategories = [[NSMutableArray alloc] init];
I think your supposed to assign properties using the "." notation (except in init methods, or where you can be absolutely sure it's not got a memory-allocated value already), even when available as a local variable:
Code:
self.newsCategories = [[NSMutableArray alloc] init];
I could be wrong though... I'm a bit new to properties myself.
Last edited by Son of a Beach; 05-20-2010 at 09:32 PM .
05-20-2010, 09:23 PM
#7 (permalink )
Emphasizing Fundamentals
Join Date: Jul 2009
Location: NoVA / DC Area
Age: 36
Posts: 7,990
See the properties link in my signature for more info.
05-21-2010, 05:26 PM
#8 (permalink )
Registered Member
Join Date: May 2010
Posts: 35
The leak was happening in the didStartElement method. I was copying a string without releasing it.
Code:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
NewsCurrentElement = [[elementName copy] autorelease] ;
if ([elementName isEqualToString:NewsItemType])
{
// clear out our story item caches...
NewsItem = [[NSMutableDictionary alloc] init];
NewsCurrentElement1 = [[NSMutableString alloc] init];
NewsCurrentElement2 = [[NSMutableString alloc] init];
if(NewsNumElements == 3)
{
NewsCurrentElement3 = [[NSMutableString alloc] init];
}
}
}
Thread Tools
Display Modes
Linear Mode
Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
» Advertisements
» Online Users: 309
9 members and 300 guests
Abidullah , ajay123123 , Fstuff , guusleijsten , HemiMG , newDev , pkIDSF , Sami Gh , Steven.C
Most users ever online was 1,387, 04-10-2012 at 04:21 AM.
» Stats
Members: 175,648
Threads: 94,113
Posts: 402,877
Top Poster: BrianSlick (7,990)
Welcome to our newest member, brandon6031