【问题标题】:iOS - NJSONSerialization causing a memory leak??(according to Instruments)iOS - NJSONSerialization 导致内存泄漏??(根据 Instruments)
【发布时间】:2013-11-30 10:25:42
【问题描述】:

下面是带有代码的方法,其中我使用手动内存管理出现内存泄漏。内存泄漏是通过使用 Xcode 工具检测到的,并且特别指向我正在使用 NSJSONSerialization 的行。我正在运行目标应用程序(在装有 iOS 6.1 的设备上)。

我第一次点击刷新按钮时没有泄漏。任何后续点击都会产生泄漏(如果我继续点击按钮,则会产生更多泄漏)。下面是代码 - 这是使用 JSON Web 服务的基本内容(Web 服务链接是伪造的,但我使用的真实链接有效)。您会注意到我正在使用 Grand Central Dispatch,这样我就可以更新 UI 而无需等待 JSON 的解析完成。

仪器检测到的线被星号包围。我想为可能对这里发生的事情有所了解的任何人提供帮助。完整的堆栈跟踪(如下 cmets 中提到的,我将放在这里:)

+(NSJSONSerialization JSONObjectWithData:option:error:] -> -[_NSJSONReader parseData:options:] -> -[_NSJSONReader parseUTF8JSONData:skipBytes:options]->newJSONValue->newJSONString->[NSPlaceholde‌​rString initWithBytes:length:encoding:]

-(void)parseDictionary:(NSDictionary *)dictonary{

self.transactions = [dictonary objectForKey:@"transactions"];
if(!self.transactions){
    NSLog(@"Expected 'transactions' array");
    return;
}

for (int arrayIndex = 0; arrayIndex < [self.transactions count]; arrayIndex++) {
    TransactionResult *result = [[[TransactionResult alloc] init] autorelease];
    result.transactionID = [[self.transactions objectAtIndex:arrayIndex] objectForKey:@"ID"];
    result.transactionDescription = [[self.transactions objectAtIndex:arrayIndex] objectForKey:@"description"];
    result.transactionPrice = [[self.transactions objectAtIndex:arrayIndex] objectForKey:@"price"];
    self.totalPrice += [result.transactionPrice doubleValue];
    NSLog(@"total price: %f", self.totalPrice);
    [self.transactionResults addObject:result];
    result = nil;
}

}

 - (IBAction)refreshButtonPressed:(UIBarButtonItem *)sender {
        __block id resultObject;
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
        NSURL *url = [NSURL URLWithString:@"http://mywebservice.php"];
        NSData *data = [NSData dataWithContentsOfURL:url];

        NSError *error;
        ***resultObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];***

        if(!error){
            if([resultObject isKindOfClass:[NSDictionary class]]){
                NSDictionary *dictonary = resultObject;
                [self parseDictionary:dictonary];
                NSLog(@"Done parsing!");
                dispatch_async(dispatch_get_main_queue(), ^{
                    self.isLoading = NO;
                    [self.transactionsTableView reloadData];
                });
            }
            else{
                NSLog(@"JSON Error: Expected Dictionary");
                resultObject = nil;
                return;
            }
        }
        else{
            NSLog(@"JSON Error: %@", [error localizedDescription]);
            dispatch_async(dispatch_get_main_queue(), ^{
                resultObject = nil;
                [self.transactionsTableView reloadData];
                [self showError];
            });
            return;
        }
    });
    }

【问题讨论】:

  • 我的第一个问题始终是您是否有不能切换到 ARC 的原因。
  • 因为不幸的是我必须支持 iOS 4.3。
  • 在这种情况下,好消息... iOS 4.3 支持 ARCLite。 ARCLite 是没有归零弱引用的 ARC。见Objective-C Feature Availability Index
  • 那很好 - 感谢您的链接。但是,我想找出手头的问题,而不是想办法解决它。
  • 好吧,努力吧... :-) parseDictionary 是什么样的?另外,我认为您不需要在第一个else 分支中的resultObject 上调用release,因为它应该是自动释放的。是否有任何 else 分支被执行?

标签: ios memory-management memory-leaks nsjsonserialization xcode-instruments


【解决方案1】:

我在 ARC 4.3 发布后就使用了它,并在商店中放置了一个应用程序 - 关键是您可以切换到 ARC。也就是说,我试图通过创建一个应用了 no-arc 标志的类/文件来重现您的问题,但无法重现该问题。这让我相信你的问题在别处。在下面的代码中,我在另一个文件中创建了一个 Test 对象,保留它,并将测试消息发送给它。无论我将“i”设置为什么,它都会释放对象:

#import "Tester.h"

@interface Obj : NSObject <NSObject>
@end

@implementation Obj

- (id)retain
{
    NSLog(@"retain");
    id i = [super retain];
    return i;
}

- (oneway void)release
{
    NSLog(@"release");
    [super release];
}

- (void)foo
{

}

- (void)dealloc
{
    NSLog(@"Obj dealloced");
    [super dealloc];
}

@end

@implementation Tester

- (void)test
{
    int i = 2;

    __block Obj *obj;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
        obj = [[Obj new] autorelease];

        if(i == 0) {
            Obj *o = obj;
            dispatch_async(dispatch_get_main_queue(), ^
                {
                    [o foo];
                } );
        } else if(i == 1) {
            obj = nil;
        } else if(i == 2) {
            dispatch_async(dispatch_get_main_queue(), ^
                {
                    obj = nil;
                } );
        }
    } );

}

@end

【讨论】:

  • 但您没有使用 NSJSONSerialization。我不知道,但不知何故,我认为 Instruments 工具中存在错误,或者 NSJSONSerialization 发生了令人讨厌的事情。请查看 Instruments 的调用堆栈,它会告诉您问题出在哪里。我一直在检查我的代码,但还没有看到我的任何 NSString 泄漏在哪里,并且 Analyzer 没有发现任何错误。还有什么我可能需要寻找的,或者 Instruments 中是否有任何东西可以帮助我进一步发现问题??
  • 代码的重点是测试块保留和释放对象的各种方式 - 正如我在非 ARC 环境中所说的那样 - 最初保留对象,然后在最后释放,即使您将块中间的变量归零。您没有为 [self parseDictionary:dictionary] 提供代码,您是否先发布然后保留新词典?此外,您的架构确实不是最好的 - 您正在使用 Apple 便利例程在并发队列上执行同步后台获取,所有这些都是为了避免使用您自己的 NSURLConnection + 回调。
猜你喜欢
  • 2021-04-20
  • 2017-05-26
  • 1970-01-01
  • 1970-01-01
  • 2011-06-28
  • 2011-06-07
  • 2015-07-06
  • 2014-06-07
相关资源
最近更新 更多