Утечка памяти и сбой приложения при возврате к списку альбомов - PullRequest
1 голос
/ 06 февраля 2012

Я использую Three20 для создания просмотра изображений. Сначала я создаю список альбомов из базы данных sql, и когда пользователь выбирает какой-либо альбом, его строка URL-адреса передается в этот код, который создает множество доступных картинок в сети с использованием XML Parser. все работает нормально, но когда пользователь возвращается к списку альбомов и выбирает другой альбом. сбой приложения с 'Тема 1: Программа получила сингл: "EXC + BAD_ACCESS" в main.m . плюс XCode Product Analyze дает потенциальную утечку памяти, когда я создаю photoSource в viewDidLoad. Вот код

#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

и вот didSelectRowAtIndexPath, который выдвигает это представление

- (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];   
}

Вот код для 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

использовал этот урок http://www.raywenderlich.com/1430/how-to-use-the-three20-photo-viewer

Нужна помощь в устранении этой утечки памяти и необходимо знать причину ее сбоя.

Спасибо

Ответы [ 2 ]

0 голосов
/ 08 февраля 2012

Утечка памяти в viewDidLoad вызвана следующей строкой:

self.photoSource = [[PhotoSource alloc]
                    initWithType:PhotoSourceNormal
                    title:self.albumName
                    photos:images
                    photos2:nil];

[PhotoSource alloc] возвращает принадлежащий вам объект (со счетом сохранения +1). initWithType:title:photos:photos2: не изменяет счет сохранения.

Таким образом, viewDidLoad остается с принадлежащим ему объектом, но без указателя на него. Чтобы сбалансировать ресурсы, вы должны отправить сообщение об автоматическом выпуске:

self.photoSource = [[[PhotoSource alloc]
                     initWithType:PhotoSourceNormal
                     title:self.albumName
                     photos:images
                     photos2:nil] autorelease];
0 голосов
/ 07 февраля 2012

Simple. В Xcode 4.0+ просто нажмите и удерживайте значок «Выполнить» и нажмите «Профиль». Это откроет инструменты, и вы захотите зомби. Затем перейдите к приложению, где раньше произошел сбой, и на этот раз оно будет отображаться в Инструментах с вызывающим абонентом и всей информацией о нем.

...