iPhone - Очередная утечка памяти в Objective-C с помощью SBJsonParser - PullRequest
0 голосов
/ 11 апреля 2011

Я действительно новичок в разработке для iPhone и вопросах переполнения стека. Я делаю свое первое приложение с января.

У моего приложения есть утечка памяти, связанная с SBJsonParser. После некоторого поиска я нашел еще один пост здесь на stackoverflow. Благодаря функции, которую Konrad77 опубликовал в своем ответе , я изменил некоторые строки своего приложения. Но я все еще получаю утечки памяти. Буду признателен за помощь. Я использую AsiHttpRequest 1.8 и JSONframework 3.0beta1.

Приборы сообщают мне, что утечка находится в следующей строке MyLists.m на 99,2%:

resultObject = [self.model JSONObjectForRequest:request];

Остальные 0,8% идут на следующую строку MyLists.m:

[self.model.myLists addObject:userData];

Обе предыдущие строки находятся внутри функции listaGetRequestOnResult. Здесь у вас есть все связанные код:

-MyLists.h:

    #import <UIKit/UIKit.h>
    #import "Model.h"
    #import "ASIFormDataRequest.h"

    @interface MyLists : UITableViewController {
        Model *model;
        NSObject *resultObject;
    }

    @property (assign) Model *model;
    @property (nonatomic,assign) NSObject *resultObject;

    @end

-MyLists.m:

#import "MyLists.h"
#import "ASIFormDataRequest.h"

@implementation MyLists


@synthesize model;
@synthesize resultObject;

-(void)loadListData {
    [self showWaitPopup:CARGANDO];

    //Remote listaGet operation
    NSURL *url = [NSURL URLWithString:self.model.operationsURL];

    ASIFormDataRequest *postRequest = [ASIFormDataRequest requestWithURL:url];
    [postRequest setPostValue:@"listaGet" forKey:@"action"];
    [postRequest setPostValue:@"JSON" forKey:@"format"];

    [postRequest setDelegate:self];
    [postRequest setDidFinishSelector:@selector(listaGetRequestOnResult:)];
    [postRequest setDidFailSelector:@selector(listaGetRequestOnFault:)];
    [postRequest startAsynchronous];
}

- (void)listaGetRequestOnResult:(ASIFormDataRequest *)request {
    [self hideWaitPopup:CARGANDO];

    resultObject = [self.model JSONObjectForRequest:request];

    NSDictionary *data = (NSDictionary *)resultObject;
    NSNumber *errorCode = [data valueForKey:@"errorCode"];
    if ([errorCode intValue] == 0) {
        //Remote operation did end successfully
        NSMutableArray *userData = [data valueForKey:@"data"];

        //Set list into model For now, only one component for the table
        [self reinitializeTableList:FALSE];
        self.model.myLists = [[NSMutableArray alloc] init];
        [self.model.myLists addObject:userData];
        [self.model.myLists retain];
    } else {
        //Remote operation did end succesfully but returned and error
        [model reportError:[data valueForKey:@"errorText"] withTitle:@"Error"];

        [self reinitializeTableList:FALSE];
    }
    [self.tableView reloadData];
}

- (void)listaGetRequestOnFault:(ASIFormDataRequest *)request {
    [self hideWaitPopup:CARGANDO];

    NSError *error = [request error];
    [model reportError:[error localizedDescription] withTitle:@"Error de conexión"];

    [self reinitializeTableList:TRUE];
}

-(void)reinitializeTableList:(BOOL)reloadTableData {
    if (self.model.myLists) {
        [self.model.myLists release];
    }
    self.model.myLists = nil;
    if (reloadTableData) {
        [self.tableView reloadData];
    }
}

- (void)viewDidLoad {
    self.model = [Model getModel];

    [super viewDidLoad];
}

- (void)viewWillAppear:(BOOL)animated {
    [self loadListData];
    [super viewWillAppear:animated];
}

- (void)dealloc {
    model = nil;
    resultObject = nil;
    [super dealloc];
}


@end

-Model.h:

#import <Foundation/Foundation.h>
#import "ASIHTTPRequest.h"

@interface Model : NSObject {
    NSString *operationsURL;
    NSString *imagesBaseURL;
    NSMutableArray *myLists;
}

@property (retain) NSString *operationsURL;
@property (retain) NSString *imagesBaseURL;
@property (retain) NSMutableArray *myLists;

+ (Model*) getModel;
//+ (id) allocWithZone:(NSZone *) zone;
+ (void) initModel;
- (void)reportError:(NSString*)mensaje withTitle:(NSString*)withTitle;
- (NSObject*)JSONObjectForRequest:(ASIFormDataRequest *)request;

@end

-Model.m:

#import "Model.h"
#import "ASIHTTPRequest.h"
#import "JSON.h"

@implementation Model

static Model *uniqueInstance = nil;

@synthesize operationsURL;
@synthesize imagesBaseURL;
@synthesize myLists;

+ (Model*) getModel {
    @synchronized(self) {
        if (uniqueInstance == nil) {
            uniqueInstance = [[Model alloc] init];
            [self initModel];
        }
    }
    return uniqueInstance;
}

/*+ (id) allocWithZone:(NSZone *) zone {
    @synchronized(self) {
        if (uniqueInstance == nil) {
            uniqueInstance = [super allocWithZone:zone];
            return uniqueInstance;
        }
    }
    return nil;
}*/

+ (void) initModel {
    //URL
    uniqueInstance.operationsURL=[NSString stringWithFormat:@"SOME_URL"];
    uniqueInstance.imagesBaseURL=[NSString stringWithFormat:@"SOME_URL"];
}

-(void)reportError:(NSString*)mensaje withTitle:(NSString*)withTitle {
    UIAlertView *alertDialog;
    alertDialog = [[UIAlertView alloc] initWithTitle:withTitle
                                             message:[NSString stringWithFormat:@"%@",mensaje]
                                            delegate: nil 
                                   cancelButtonTitle: @"Aceptar"
                                   otherButtonTitles:nil];

    [alertDialog show];
    [alertDialog release];
}

- (NSObject*)JSONObjectForRequest:(ASIFormDataRequest *)request {
    SBJsonParser *jsonParser = [SBJsonParser new];
    NSObject *object=[jsonParser objectWithString:[request responseString] error:nil];
    if (object == nil) {
        [self reportError:[jsonParser error] withTitle:@"Error librería JSON"];
    }
    [jsonParser release], jsonParser = nil;
    return object;
}

- (void)dealloc {
    [operationsURL release];
    [imagesBaseURL release];
    [myLists release];
    [super dealloc];
}

@end

Здесь у вас есть скриншоты инструментов:

Instruments leaks 1

Instruments leaks 2

Заранее спасибо!

Ответы [ 2 ]

2 голосов
/ 12 апреля 2011

Ваша утечка (на самом деле она имеет два дополнительных удержания):

self.model.myLists = [[NSMutableArray alloc] init];
[self.model.myLists addObject:userData];
[self.model.myLists retain];

Вы, вероятно, хотите что-то вроде этого:

self.model.myLists = [NSMutableArray arrayWithObject:userData];

Я бы также не использовал assign свойства, как вы это сделали.

1 голос
/ 12 апреля 2011

Отличный код для размещения работы и большая пятерка за использование инструментов. Я постоянно удивляюсь, сколько разработчиков не используют его.

Я знаю, что вы, вероятно, уже читали это, но, пожалуйста, перечитайте это;

http://developer.apple.com/library/ios/#referencelibrary/GettingStarted/Learning_Objective-C_A_Primer/_index.html%23//apple_ref/doc/uid/TP40007594

Код, подобный этому;

self.model.myLists = [[NSMutableArray alloc] init];
[self.model.myLists addObject:userData];
[self.model.myLists retain];

показывает, что вы не поняли концепцию инкапсуляции.

Очень важно, чтобы model отвечал за этот список, а не за внешний объект. Добавьте код в свой класс модели, чтобы создать этот список в методе init, затем добавьте методы, которые вы можете вызвать и которые добавят элементы в этот список.

Вызов alloc означает, что у вас есть одно удержание в этой области, затем повторный вызов retain означает, что у вас есть два. Я уверен, что есть и другие подобные проблемы.

Чтобы понять правила управления памятью, прочитайте это;

http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html%23//apple_ref/doc/uid/20000994-BAJHFBGH

Совет tc выше - золотой, не используйте asign, если вы не понимаете, почему вы его используете.

http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html%23//apple_ref/doc/uid/TP30001163-CH17-SW1

содержит более подробную информацию о значении различных опций.

...