iPhone - SBJsonParser 的另一个 Objective-C 内存泄漏

Posted

技术标签:

【中文标题】iPhone - SBJsonParser 的另一个 Objective-C 内存泄漏【英文标题】:iPhone - Another Objective-C Memory leak with SBJsonParser 【发布时间】:2011-04-11 18:09:23 【问题描述】:

我对 iPhone 开发和 Stack Overflow 问题非常陌生。自一月份以来,我一直在做我的第一个应用程序。

我的应用存在与 SBJsonParser 相关的内存泄漏。经过一番谷歌搜索后,我在 *** 上找到了另一个 post。感谢 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 的截图:

提前致谢!

【问题讨论】:

这可能是在单独的线程上吗? 你能用 [[SBJsonParser alloc] init] 代替 [SBJsonParser new] 吗? @Richard J. Ross III: 我没有在我的应用程序上编写线程我不知道这是否有帮助但是我第一次调用 model = [Model getModel]; 是在我的 AppDelegate @Zaky German: 的函数 didFinishLaunchingWithOptions 我已经尝试过 [[SBJsonParser alloc] init] 并且我仍然遇到相同的内存泄漏我还尝试将 jsonParser 设置为模型中的保留属性然后我添加了如果:if (!jsonParser) jsonParser = [[SBJsonParser alloc] init]; 但我不工作 【参考方案1】:

你的泄漏(它实际上有两个额外的保留):

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

你可能想要这样的东西:

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

我也不会像你那样使用assign 属性。

【讨论】:

非常感谢 @tc。这帮助我摆脱了我的应用程序上的内存泄漏。在阅读了 @BillDudney 答案的链接后,我明白了为什么你告诉我要避免使用assignattribute。所以我将使用retain 代替(modelresultObject)。 当然,那你需要把dealloc改成self.model = nilself.resultObject = nil,或者等效的。【参考方案2】:

很棒的职位发布代码,以及使用 Instruments 的前五名。我一直很惊讶有多少开发人员不使用它。

我知道您可能已经阅读过这篇文章,但请重新阅读;

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,然后再次调用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

对各种选项的含义有更多详细信息。

【讨论】:

非常感谢 @BillDudney。我非常感谢您的建议,因为它们帮助我理解了我的错误和 @tc. 的回答。

以上是关于iPhone - SBJsonParser 的另一个 Objective-C 内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

重复符号_OBJC_METACLASS_$_SBJsonParser

重复符号错误:SBJsonParser.o?

无法收到ios通知

将手指从按钮滑动到 iPhone 中的另一个按钮

显示当前位置,以及 iphone 中的另一个位置

如何访问实体列表中的另一列?