Iphone Programming XML Parser NSMutableArray - iphone

I am currently having a problem with parsing information from an XML into an NSMutableArray, i have pasted the code below which is for the parser this retrieves the document off the internet and constructs the array. I am then trying to copy the items from the xml parser into an NSMutableArray for searching, but every time i try to copy the array it returns a struct error so i have removed the changes for now.
Here is the code for the parser and the current code for the viewdidload in the main view controller, if anyone can help i would really appreciate it.
#import "BlogRssParser.h"
#import "BlogRss.h"
#implementation BlogRssParser
#synthesize currentItem = _currentItem;
#synthesize currentItemValue = _currentItemValue;
#synthesize rssItems = _rssItems;
#synthesize delegate = _delegate;
#synthesize retrieverQueue = _retrieverQueue;
- (id)init{
if(![super init]){
return nil;
}
_rssItems = [[NSMutableArray alloc]init];
return self;
}
- (NSOperationQueue *)retrieverQueue {
if(nil == _retrieverQueue) {
_retrieverQueue = [[NSOperationQueue alloc] init];
_retrieverQueue.maxConcurrentOperationCount = 1;
}
return _retrieverQueue;
}
- (void)startProcess{
SEL method = #selector(fetchAndParseRss);
[[self rssItems] removeAllObjects];
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self
selector:method
object:nil];
[self.retrieverQueue addOperation:op];
[op release];
}
-(BOOL)fetchAndParseRss{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
//To suppress the leak in NSXMLParser
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
NSURL *url = [NSURL URLWithString:#"http://xxxxxxxxx.co.uk/rssparser/Data.xml"];
BOOL success = NO;
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[parser setDelegate:self];
[parser setShouldProcessNamespaces:YES];
[parser setShouldReportNamespacePrefixes:YES];
[parser setShouldResolveExternalEntities:NO];
success = [parser parse];
[parser release];
[pool drain];
return success;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict{
if(nil != qualifiedName){
elementName = qualifiedName;
}
if ([elementName isEqualToString:#"item"]) {
self.currentItem = [[[BlogRss alloc]init]autorelease];
} else if([elementName isEqualToString:#"title"] ||
[elementName isEqualToString:#"description"] ||
[elementName isEqualToString:#"mediaUrl"] ||
[elementName isEqualToString:#"link"] ||
[elementName isEqualToString:#"guid"] ||
[elementName isEqualToString:#"pubDate"]) {
self.currentItemValue = [NSMutableString string];
} else {
self.currentItemValue = nil;
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if(nil != qName){
elementName = qName;
}
if([elementName isEqualToString:#"title"]){
self.currentItem.title = self.currentItemValue;
}else if([elementName isEqualToString:#"description"]){
self.currentItem.description = self.currentItemValue;
}else if([elementName isEqualToString:#"link"]){
self.currentItem.linkUrl = self.currentItemValue;
}else if([elementName isEqualToString:#"mediaUrl"]){
self.currentItem.mediaUrl = self.currentItemValue;
}else if([elementName isEqualToString:#"guid"]){
self.currentItem.guidUrl = self.currentItemValue;
}else if([elementName isEqualToString:#"pubDate"]){
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
self.currentItem.pubDate = [formatter dateFromString:self.currentItemValue];
[formatter release];
}else if([elementName isEqualToString:#"item"]){
[[self rssItems] addObject:self.currentItem];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(nil != self.currentItemValue){
[self.currentItemValue appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock{
//Not needed for now
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{
if(parseError.code != NSXMLParserDelegateAbortedParseError) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[(id)[self delegate] performSelectorOnMainThread:#selector(processHasErrors)
withObject:nil
waitUntilDone:NO];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
[(id)[self delegate] performSelectorOnMainThread:#selector(processCompleted)
withObject:nil
waitUntilDone:NO];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
-(void)dealloc{
self.currentItem = nil;
self.currentItemValue = nil;
self.delegate = nil;
[_rssItems release];
[super dealloc];
}
#end
BlogRSSParser.h
#import <Foundation/Foundation.h>
#class BlogRss;
#protocol BlogRssParserDelegate;
#interface BlogRssParser : NSObject <NSXMLParserDelegate>{
BlogRss * _currentItem;
NSMutableString * _currentItemValue;
NSMutableArray * _rssItems;
id<BlogRssParserDelegate> _delegate;
NSOperationQueue *_retrieverQueue;
}
#property(nonatomic, retain) BlogRss * currentItem;
#property(nonatomic, retain) NSMutableString * currentItemValue;
#property(readonly) NSMutableArray * rssItems;
#property(nonatomic, assign) id<BlogRssParserDelegate> delegate;
#property(nonatomic, retain) NSOperationQueue *retrieverQueue;
- (void)startProcess;
#end
#protocol BlogRssParserDelegate <NSObject>
-(void)processCompleted;
-(void)processHasErrors;
#end
ViewDidLoad
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
[self toolbarInit];
_rssParser = [[BlogRssParser alloc]init];
self.rssParser.delegate = self;
[[self rssParser]startProcess];
}

You rssItems array is marked "readonly".
Change:
#property(readonly) NSMutableArray * rssItems;
to
#property(nonatomic, retain) NSMutableArray * rssItems;

Related

Memory Leak & App Crash when going back to Album List

I'm using three20 to create image viewer. First i'm creating list of albums from the sql db and when user selects any album, its url string is passed to the this code that creates thums of available pics on network using XML Parser. everything works fine but when user goes back to the album list and selects another album. app crashes with 'Thread 1: Program received singal: "EXC+BAD_ACCESS" in main.m. plus XCode Product Analyze gives potential memory leak where i'm creating photoSource in the viewDidLoad. Here is the code
#import "AlbumController.h"
#import "PhotoSource.h"
#import "Photo.h"
#import "AlbumInfo.h"
#import "AlbumDatabase.h"
#implementation AlbumController
#synthesize albumName;
#synthesize urlAddress;
#synthesize images;
- (void)viewDidLoad
{
[super viewDidLoad];
// NSLog(#"%#", self.urlAddress);
[self createPhotos]; // method to set up the photos array
self.photoSource = [[PhotoSource alloc]
initWithType:PhotoSourceNormal
title:self.albumName
photos:images
photos2:nil];
self.navigationController.navigationBar.tintColor = [UIColor blackColor];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// release and set to nil
}
-(void)createPhotos
{
if ([stories count] == 0)
{
NSString *path = self.urlAddress;
[self parseXMLFileAtURL:path];
}
images = [NSMutableArray arrayWithCapacity:[stories count]]; // needs to be mutable
for (int i = 0; i < [stories count]; i++)
{
NSString *img = [[stories objectAtIndex:i] objectForKey:#"image"];
img = [img stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
//NSString * caption = [[stories objectAtIndex:i] objectForKey:#"caption"];
//caption = [caption stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
[images addObject:[[[Photo alloc] initWithURL:img smallURL:img size:CGSizeMake(320, 212)] autorelease]];
}
}
#pragma mark -
#pragma mark XML Parser Implementation
- (void)parserDidStartDocument:(NSXMLParser *)parser{
//NSLog(#"found file and started parsing");
}
- (void)parseXMLFileAtURL:(NSString *)URL
{
stories = [[NSMutableArray alloc] init];
//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
rssParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[rssParser setDelegate:self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[rssParser setShouldProcessNamespaces:NO];
[rssParser setShouldReportNamespacePrefixes:NO];
[rssParser setShouldResolveExternalEntities:NO];
[rssParser parse];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSString * errorString = [NSString stringWithFormat:#"Unfortunately it is not possible to load Pictures. Please check Internet Connection. (Error code %i )", [parseError code]];
//NSLog(#"error parsing XML: %#", errorString);
UIAlertView * errorAlert = [[UIAlertView alloc] initWithTitle:#"Failed to load the feed." message:errorString delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
[errorAlert release];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
//NSLog(#"found this element: %#", elementName);
currentElement = [elementName copy];
if ([elementName isEqualToString:#"item"]) {
// clear out our story item caches...
item = [[NSMutableDictionary alloc] init];
currentCaption = [[NSMutableString alloc] init];
//currentThumbnail = [[NSMutableString alloc] init];
currentImage = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
//NSLog(#"ended element: %#", elementName);
if ([elementName isEqualToString:#"item"]) {
// save values to an item, then store that item into the array...
//[item setObject:currentThumbnail forKey:#"thumbnail"];
//[item setObject:currentCaption forKey:#"caption"];
[item setObject:currentImage forKey:#"image"];
[stories addObject:[[item copy] autorelease]];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
// save the characters for the current item...
if ([currentElement isEqualToString:#"thumbnail"]) {
//[currentThumbnail appendString:string];
}// else if ([currentElement isEqualToString:#"caption"]) {
//[currentCaption appendString:string];
//}
else if ([currentElement isEqualToString:#"image"]) {
[currentImage appendString:string];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
NSLog(#"all done!");
NSLog(#"stories array has %d items", [stories count]);
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
if (toInterfaceOrientation == UIInterfaceOrientationPortrait ||
toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
return YES;
}
else
{
return NO;
}
}
#pragma mark -
#pragma mark Memory Management
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (void)dealloc {
[currentElement release];
[rssParser release];
[stories release];
[item release];
[currentCaption release];
//[currentThumbnail release];
[currentImage release];
[images release];
[stories release];
[super dealloc];
}
#end
and here is the didSelectRowAtIndexPath thats pushing this view
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
AlbumInfo *info = [_albumInfos objectAtIndex:indexPath.row];
AlbumController *albumController = [[AlbumController alloc] init];
albumController.urlAddress = info.address;
albumController.albumName = info.name;
[self.navigationController pushViewController:albumController animated:YES];
[albumController release];
}
Here is the code for AlbumController.h
#import <Foundation/Foundation.h>
#import "Three20/Three20.h"
#interface AlbumController : TTThumbsViewController <NSXMLParserDelegate>
{
NSString *albumName;
NSString *urlAddress;
// images
NSMutableArray *images;
// parser
NSXMLParser * rssParser;
NSMutableArray * stories;
NSMutableDictionary * item;
NSString * currentElement;
NSMutableString * currentImage;
NSMutableString * currentCaption;
}
#property (nonatomic, strong) NSString *albumName;
#property (nonatomic, strong) NSString *urlAddress;
#property (nonatomic, retain) NSMutableArray *images;
- (void)createPhotos;
- (void)parseXMLFileAtURL:(NSString *)URL;
#end
used this tutorial http://www.raywenderlich.com/1430/how-to-use-the-three20-photo-viewer
Need help solving this memory leak and need to know why its crashing.
Thanks
Simple. In Xcode 4.0+, Just click-hold on the Run icon, and press Profile. It'll open up Instruments, and you'll want Zombies. Then navigate your app to where the crash happened before, and this time, it'll show up in Instruments with the caller, and all the information about it.
The memory leak in viewDidLoad is caused by the following line:
self.photoSource = [[PhotoSource alloc]
initWithType:PhotoSourceNormal
title:self.albumName
photos:images
photos2:nil];
[PhotoSource alloc] returns an object you own (with a retain count of +1).
initWithType:title:photos:photos2: does not change the retain count.
So viewDidLoad is left with an object it owns, but no pointer to it. To balance the alloc you should send an autorelease message:
self.photoSource = [[[PhotoSource alloc]
initWithType:PhotoSourceNormal
title:self.albumName
photos:images
photos2:nil] autorelease];

Application start debuging at pool drain instruction

I am build an iPhone application who try to perform a login via a Rest web service (WCF).
The part who call the service work correctly. I receive data from the called service without any problem.
But I have to parse the message using the NSXMLParser. When I introduce it in my application (this is the first time I used it) the application parse the message correctly but at the end of the application execution it's lunch gdb and pause the application at the start of [pool drain] instruction.
#interface MessageParser : NSObject <NSXMLParserDelegate> {
NSMutableString* currentProperty;
Message* message;
}
#property (nonatomic, retain) NSMutableString* currentProperty;
#property (nonatomic, retain) Message* message;
- (void)parseMessageData:(NSData *)data parseError:(NSError **)err;
#end
#implementation MessageParser
#synthesize message, currentProperty;
- (void)parseMessageData:(NSData *)data parseError:(NSError **)err {
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
if (err && [parser parserError]) {
*err = [parser parserError];
}
[parser release];
}
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
if(qName){
elementName = qName;
}
if([elementName isEqualToString:#"Message"]){
self.message = [[Message alloc] init];
}
else if( [elementName isEqualToString:#"body"] || [elementName isEqualToString:#"code"] || [elementName isEqualToString:#"error"]){
currentProperty = [NSMutableString string];
}
}
-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if(qName){
elementName = qName;
}
if(message){
if([elementName isEqualToString:#"body"]){
self.message.messageBody = currentProperty;
}
else if([elementName isEqualToString:#"error"]){
self.message.error = currentProperty;
}
else if([elementName isEqualToString:#"code"]){
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
self.message.messageCode = [f numberFromString:currentProperty];
[f release];
}
}
self.currentProperty = nil;
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)foundedCharacters
{
if (self.currentProperty) {
[currentProperty appendFormat:#"%#", foundedCharacters];
}
}
Some time the debugger give me this message : Attempting to create USE_BLOCK_IN_FRAME variable with block that isn't in the frame.
Do you have any idea ?
Thank you.
This can be done using method swizzling:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface NSAutoreleasePool(customDrain)
-(void) myDrain;
#end
#implementation NSAutoreleasePool(customDrain)
-(void) myDrain
{
if ([self respondsToSelector:#selector(myDrain)])
{
NSLog(#"Draining...");
// note that #selector(myDrain) was remapped
// to #selector(drain) during the process of
// the swizzling, so now, myDrain calls drain,
// and drain calls myDrain.
[self myDrain];
}
else
{
NSLog(#"Remapping Failed!");
}
}
#end
void SwizzleMethod(Class c, SEL orig, SEL new);
void SwizzleMethod(Class c, SEL orig, SEL new) {
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if (origMethod && newMethod)
method_exchangeImplementations(origMethod, newMethod);
}
int main (int argc, const char * argv[])
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
SwizzleMethod([NSAutoreleasePool class], #selector(drain), #selector(myDrain));
[pool drain];
return 0;
}
You simply put your own code in the category's myDrain method, and, then, you run your own code when the pool is drained.
I hope this helped.

XMLParser Advice

I'm not sure what I am doing wrong. I have a URL leading to an XML tree that looks like:
<result>
...
<title>
...
</title>
<body>
...
</body>
...
</result>
I just need to parse the file and get the title and body. Here is my object.h:
#import <Foundation/Foundation.h>
#interface Object : NSObject
{
NSString *title;
NSString *description;
}
#property (nonatomic, retain) NSString *title;
#property (nonatomic, retain) NSString *description;
#end
And here is Object.m:
#import "Object.h"
#implementation Object
#synthesize title, description;
-(void) dealloc
{
[title release];
[description release];
[super dealloc];
}
#end
Here is my XMLParser.h:
#import <Foundation/Foundation.h>
#import "Object.h"
#interface XMLParser : NSObject <NSXMLParserDelegate>
{
NSMutableString *currentNodeContent;
NSMutableArray *arrayOfObjects;
NSXMLParser *parser;
Object *currentObject;
}
#property (readonly, retain) NSMutableArray *arrayOfObjects;
-(id) loadXMLbyURL:(NSString *)urlString;
#end
And finally, XMLParser.m:
#import "XMLParser.h"
#import "Object.h"
#implementation XMLParser
#synthesize arrayOfObjects;
-(id) loadXMLbyURL:(NSString *)urlString
{
arrayOfObjects = [[NSMutableArray alloc] init];
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [[[NSData alloc] initWithContentsOfURL:url] autorelease];
parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
return self;
}
-(void) dealloc
{
[parser release];
[super dealloc];
}
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"result"])
{
currentObject = [Object alloc];
}
}
-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"title"])
{
currentObject.title = currentNodeContent;
}
if([elementName isEqualToString:#"body"])
{
currentObject.description = currentNodeContent;
}
if([elementName isEqualToString:#"result"])
{
[arrayOfObjects addObject:currentObject];
[currentObject release];
currentObject = nil;
[currentNodeContent release];
currentNodeContent = nil;
}
}
-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
currentNodeContent = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
#end
In my main code I make a call to XML parser like this:
xmlParser = [[XMLParser alloc] loadXMLbyURL:#"www.websiteWithXML.com"];
However nothing is being placed into my arrayOfObjects (I know this because when I tell a tableview to have as many rows as my array, there are no rows).
please help!!! Thank you in advance!
Not sure if this will help...
Try changing:
arrayOfObjects = [[NSMutableArray alloc] init];
To:
arrayOfObjects = [[NSMutableArray alloc] initWithCapacity:0];
Other than that it appears that your code is fine. Also just because your table isn't loading anything doesn't mean that you do not have objects in your array. Use breakpoints to take a look at your data after you finish parsing and before you try to load your tableview.
Try resetting currentNodeContent inside each of your element starts. For example:
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"result"])
{
currentObject = [Object alloc];
}
else if ([elementName isEqualToString:#"title"] || [elementName isEqualToString:#"body"])
{
[currentNodeContent setString:#""];
}
}
Then, when you receive characters do an append:
-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[currentNodeContent appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
By doing this, you handle the situation where a foundCharacters gets multiple times to capture all of the characters within a given element.

NSXMLParser & memory leaks

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 :NSXMLParser Leaking
-(BOOL)fetchAndParseRss{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
//To suppress the leak in NSXMLParser
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
NSURL *url = [NSURL URLWithString:GALLERY_FEED];
BOOL success = NO;
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[parser setDelegate:self];
[parser setShouldProcessNamespaces:YES];
[parser setShouldReportNamespacePrefixes:YES];
[parser setShouldResolveExternalEntities:NO];
success = [parser parse];
[parser release];
[pool drain];
return success;
}
As found on this page, you can do the following to get rid of the leaks as well (I've found that you don't need the setMemoryCapacity or setDiskCapacity calls):
NSData *xml = [NSData
dataWithContentsOfURL: [NSURL
URLWithString:#"http://some-xml-url.com/my.xml"]];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xml];

Iphone 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
#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 create an instance of the class I do like so:
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:
http://img139.imageshack.us/img139/3997/leaks.png
For the life of me I can't figure out where these leaks are coming from. Any help would be greatly appreciated.
The leak occurred in the didStartElement method. I was copying elementName without releasing it.
- (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];
}
}
}
You might also want to release (if necessary) the allocated NSMutableString properties before allocating another NSMutableString into the property like so:
- (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];
}
}
}