Memory Leak NSCFString Iphone NSXMLParser
I am creating an application that parses an rss feed. There are two different feed types in the application, with different names for the items in the feed, so I created an NSXMLParser NSObject that takes the name of the items in each feed before parsing. Here is my code:
NewsFeedParser.h
#import
@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
#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 instantiate a class, I do this:
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 application with the leak tool, the leaks point to the [NSXMLNewsParser parse] call in NewsFeedParser.m.
Here is a screenshot of the Leaks tool with the NSCFStrings leaked:
http://img139.imageshack.us/img139/3997/leaks.png
In my life I cannot understand where these leaks come from. Any help would be greatly appreciated.
a source to share
The leak occurred in the didStartElement method. I copied elementName without letting go.
- (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];
}
}
}
a source to share
You can also free (if necessary) the highlighted properties NSMutableString
before highlighting another NSMutableString
in the property:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if (NewsCurrentElement) {
[NewsCurrentElement release], NewsCurrentElement = nil;
}
NewsCurrentElement = [[elementName copy] autorelease];
if ([elementName isEqualToString:NewsItemType]) {
// clear out our story item caches...
if (NewsItem) {
[NewsItem release], NewsItem = nil;
}
NewsItem = [[NSMutableDictionary alloc] init];
if (NewsCurrentElement1) {
[NewsCurrentElement1 release], NewsCurrentElement1 = nil;
}
NewsCurrentElement1 = [[NSMutableString alloc] init];
if (NewsCurrentElement2) {
[NewsCurrentElement2 release], NewsCurrentElement2 = nil;
}
NewsCurrentElement2 = [[NSMutableString alloc] init];
if(NewsNumElements == 3) {
if (NewsCurrentElement3) {
[NewsCurrentElement3 release], NewsCurrentElement3 = nil;
}
NewsCurrentElement3 = [[NSMutableString alloc] init];
}
}
}
a source to share