2011-04-11 60 views
0

我對iPhone開發和堆棧溢出問題確實很陌生。自1月份以來,我一直在做我的第一個應用。iPhone - 使用SBJsonParser的另一個Objective-C內存泄漏

我的應用程序有一個與SBJsonParser相關的內存泄漏。一些谷歌搜索後,我發現另一個post這裏在stackoverflow。由於Konrad77發佈在他的answer上的功能,我改變了我的應用程序的一些行。但我仍然有內存泄漏。我希望得到一些幫助。我使用的是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

提前感謝!

+0

這也許是在一個單獨的線程? – 2011-04-11 18:19:54

+0

你可以嘗試[[SBJsonParser alloc] init]而不是[SBJsonParser new]? – 2011-04-11 18:21:36

+0

** @理查德J.羅斯三世:**我沒有在我的應用程序上編程線程我不知道這是否有幫助但我第一次調用'model = [Model getModel];'函數'didFinishLaunchingWithOptions'我的AppDelegate ** @ Zaky德語:**我試過'[[SBJsonParser alloc] init]',我仍然得到相同的內存泄漏我也嘗試將jsonParser設置爲模型中的保留屬性然後我如果:if(!jsonParser){ \t \t jsonParser = [[SBJsonParser alloc] init]; '但我不工作 – Roger 2011-04-11 19:06:04

回答

2

你泄漏(它實際上有兩個額外的保留):

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

你可能想是這樣的:

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

我也不會用assign屬性,像你這樣做。

+0

非常感謝** @ tc **。這幫助我擺脫了我的應用程序內存泄漏。在閱讀** @ BillDudney **回答的鏈接後,我明白了爲什麼你告訴我要避免賦值屬性。所以我將使用'retain'(對'model'和'resultObject')。 – Roger 2011-04-12 10:15:14

+0

當然,那麼你需要改變dealloc來做'self.model = nil'和'self.resultObject = nil',或者等價的。 – 2011-04-12 20:49:23

1

偉大的工作發佈代碼,和使用儀器的高五大。我總是驚訝有多少開發人員不使用它。

我知道您可能已經閱讀過此內容,但請重新閱讀;

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

擁有噸的各個選項意味着更多的細節。

+0

非常感謝** @ BillDudney **。我非常感謝你的建議,因爲他們幫助我理解我的錯誤和**@tc.'s**的答案。 – Roger 2011-04-12 10:20:57