Skip to main content

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


Comments

  1. 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.

    ReplyDelete
  2. 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];

    ReplyDelete

Post a Comment

Popular posts from this blog

Why is this Javascript much *slower* than its jQuery equivalent?

I have a HTML list of about 500 items and a "filter" box above it. I started by using jQuery to filter the list when I typed a letter (timing code added later): $('#filter').keyup( function() { var jqStart = (new Date).getTime(); var search = $(this).val().toLowerCase(); var $list = $('ul.ablist > li'); $list.each( function() { if ( $(this).text().toLowerCase().indexOf(search) === -1 ) $(this).hide(); else $(this).show(); } ); console.log('Time: ' + ((new Date).getTime() - jqStart)); } ); However, there was a couple of seconds delay after typing each letter (particularly the first letter). So I thought it may be slightly quicker if I used plain Javascript (I read recently that jQuery's each function is particularly slow). Here's my JS equivalent: document.getElementById('filter').addEventListener( 'keyup', function () { var jsStart = (new Date).getTime()...

Is it possible to have IF statement in an Echo statement in PHP

Thanks in advance. I did look at the other questions/answers that were similar and didn't find exactly what I was looking for. I'm trying to do this, am I on the right path? echo " <div id='tabs-".$match."'> <textarea id='".$match."' name='".$match."'>". if ($COLUMN_NAME === $match) { echo $FIELD_WITH_COLUMN_NAME; } else { } ."</textarea> <script type='text/javascript'> CKEDITOR.replace( '".$match."' ); </script> </div>"; I am getting the following error message in the browser: Parse error: syntax error, unexpected T_IF Please let me know if this is the right way to go about nesting an IF statement inside an echo. Thank you.