根据仪器的iOS内存泄漏

Posted

技术标签:

【中文标题】根据仪器的iOS内存泄漏【英文标题】:iOS memory leak according to instruments 【发布时间】:2011-09-20 21:05:56 【问题描述】:

请帮忙!我已经阅读了内存管理规则,但也许我在某处错过了它们。 Instruments 告诉我以下代码有漏洞:

NSArray *keys = [NSArray arrayWithObjects:@"text", @"score", @"subCount", nil];
NSArray *objects 
    = [NSArray arrayWithObjects:sPlateToAdd, [
         [[NSNumber alloc] initWithInt:1] autorelease], 
         [[[NSNumber alloc] initWithInt:1] autorelease], 
         nil];

NSMutableDictionary *dPlateToAdd 
    = [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys];  // 93.4%        
[self.aFinals addObject:dPlateToAdd];    // 6.6%

Keys 和 Objects 数组没有被分配或初始化,所以我认为我不需要释放它们?

那么 Objects 里面的数字会被自动释放,所以他们没问题,不是吗? sPlateToAdd 是一个字符串,它被传递到此代码所在的方法中,所以我不是它的所有者,所以我不需要释放它。还是我?

我一定在某个地方做错了什么。

该应用程序在 iPad 上运行完全正常,但在 iPhone 3GS 上运行缓慢,我希望修复此内存泄漏可能会加快速度...

这是创建 self.aFinals 的方法,它从文本输入中传递一个字符串。我省略了一些行,但 self.aFinals 不与它们交互

-(id)initWithTerm:(NSString *)thisTerm 
    ...
    ...
    self.aFinals = [[NSMutableArray alloc] init];

    return self;

然后我有大约 5 个嵌套循环,在所有循环的中间调用 addPlateToFinals 3 次,创建 thisPlate,它变成 sPlateToAdd

// replace 1st occurance
NSString *thisPlate = [thisBase
    stringByReplacingOccurrencesOfRegex:[NSString stringWithFormat:
        @"(^[^%@]+)%@(.*$)", 
        thisChar, 
        thisChar] 
     withString:[NSString stringWithFormat:@"$1%@$2", thisSub]
     ];

     [self addPlateToFinals:thisPlate withSubCount:thisSubCount];
 // replace 2nd occurance
 thisPlate = [thisBase
     stringByReplacingOccurrencesOfRegex:[NSString stringWithFormat:
         @"(^[^%@]+%@.*)%@",    
         thisChar, 
         thisChar, 
         thisChar] 
     withString:[NSString stringWithFormat:@"$1", thisSub]
     ];

 // then it does it again, with slightly different regex

这是泄漏的完整方法:

-(void)addPlateToFinals:(NSString *)sPlateToAdd withSubCount:(NSNumber *)nSubCount 
// plate must be less than 7 characters and great than 2 chars
if (
    [sPlateToAdd length] <= [self.nPlateMax intValue] &&
    [sPlateToAdd length] >= [self.nPlateMin intValue]
    )     

    NSMutableArray *aSearchFinals = [self arrayOfFinals];

    // add plate if it is not already in the finals array   
    if(![aSearchFinals containsObject:sPlateToAdd]) 

        // filter out results that cannot be converted to valid plates
        NSPredicate *potential = [NSPredicate predicateWithFormat: @"SELF MATCHES '^[a-z]0,3[0-9]1,3[a-z]0,3$'"];
        NSPredicate *impossible1 = [NSPredicate predicateWithFormat: @"SELF MATCHES '^[a-z]2[0-9]2,3[a-z]2$'"];
        NSPredicate *impossible2 = [NSPredicate predicateWithFormat: @"SELF MATCHES '^[a-z][0-9]3$'"];
        NSPredicate *impossible3 = [NSPredicate predicateWithFormat: @"SELF MATCHES '^[a-z]2[0-9]2$'"];
        NSPredicate *impossible4 = [NSPredicate predicateWithFormat: @"SELF MATCHES '^[0-9]2[a-z]2$'"];

        if(
            [potential evaluateWithObject: sPlateToAdd] && 
            ![impossible1 evaluateWithObject: sPlateToAdd] &&
            ![impossible2 evaluateWithObject: sPlateToAdd] &&
            ![impossible3 evaluateWithObject: sPlateToAdd] &&
            ![impossible4 evaluateWithObject: sPlateToAdd]
        )                  

            NSArray *keys = [NSArray arrayWithObjects:@"text", @"score", @"subCount", nil];
            NSArray *objects = [NSArray arrayWithObjects:
                                    sPlateToAdd, 
                                    [[[NSNumber alloc] initWithInt:1] autorelease], 
                                    [[[NSNumber alloc] initWithInt:1] autorelease], 
                                    nil
                                ];

            NSDictionary *dPlateToAdd = [NSDictionary dictionaryWithObjects:objects forKeys:keys];          
            [self.aFinals addObject:dPlateToAdd];   
        
    

【问题讨论】:

什么样的对象在泄漏? 可能,skeater 意味着分配 aFinals 是@property (retain) NSMutableArray *aFinals? 您是在后台线程上执行吗?如果是这样,您有责任实例化和耗尽您自己的自动释放池。 因为你保留了你的财产,你需要像@mja建议的那样释放传递给self.aFinals的数组:self.aFinals = [NSMutableArray array]; 【参考方案1】:

您应该显示整个 `addPlateToFinals' 方法,sPlateToAdd 可能会泄漏。

基于新添加的代码self.aFinals 如果使用保留声明属性(我是 %99 是这样),则会泄漏。应该是:

self.aFinals = [[[NSMutableArray alloc] init] autorelease]

甚至更好:

self.aFinals = [NSMutableArray array]

【讨论】:

好的,但如果我的代码很糟糕,请不要恨我......我正在从 Web 开发转换。我会将其附加到问题中。 你的名声比我还高!我已经添加了方法 是的,现在......你必须告诉我你是如何调用该方法的 :)) 我特别感兴趣的是你如何创建你作为sPlateToAdd传递的对象 好的,我已经添加了对方法的调用,它在大约 5 个嵌套 for 循环的中心发生了 3 次深。我还展示了 init 函数中声明/初始化 self.aFinals 属性的行 谢谢 Valentin,我知道我要发布 self.aFinals,我只是觉得没有必要发布我分配给它的东西。【参考方案2】:

你可能有这个对象的内存泄漏,而这段代码根本不负责。

当一个对象被保留的时间多于它被释放的时间时,Cocoa/Cocoa Touch 环境中的内存泄漏就会发生。这些工具会指出它是在哪里分配的,而不是在哪里泄露的,因为这些工具无法确定哪个保留缺少发布。它们只是保留和释放;除了约定之外,没有什么能真正将它们联系在一起。

首先,进行构建和分析。这可能会指出您的代码在哪里做的不当导致泄漏。认真对待分析器警告,尤其是与内存管理相关的警告。

如果“构建和分析”无法解决您的问题,请再次通过您的应用进行分析并研究泄漏块的历史记录。这将在每次保留或释放块时向您显示。寻找没有相应释放或自动释放的保留。 (实际上,您可能会发现在没有 Instruments 的情况下,简单地阅读您的代码、寻找不平衡的保留更容易。)

【讨论】:

谢谢,分析器没有给我任何关于我的代码的警告,它只是提到了我正在使用的第三方插件中的一些不推荐使用的代码。将变量传递给方法时是否保留了变量? 除非该方法明确保留它,否则不会。但是如果那个方法把它传递给别的东西,它把它传递给别的东西,它碰巧保留它而不是稍后释放它……内存泄漏。这真的是一场狩猎。这就是区块历史如此有用的原因。【参考方案3】:

如果可能,您应该使用 NSDictonary 而不是 NSMutableDictonary,因为对象的可变版本比不可变版本占用更多内存。另外,我对代码做了一些外观增强

NSArray *keys = [NSArray arrayWithObjects:@"text", @"score", @"subCount", nil];
NSArray *objects  = [NSArray arrayWithObjects:sPlateToAdd,
                    [NSNumber numberWithInt:1], 
                    [NSNumber numberWithInt:1], 
                    nil];

NSDictonary *dPlateToAdd = [NSDictonary dictionaryWithObjects:objects forKeys:keys];  // 93.4%        
[self.aFinals addObject:dPlateToAdd];    // 6.6%

【讨论】:

这会消除内存泄漏吗? NSNumber numberWithInt:1 和我当时写的完全一样吗?感谢您的快速回复 您提供的代码中没有内存泄漏。也许,我们谈论的是分配或时间分析? NSNumber numberWithInt:1 - 你是对的,它只是更具可读性 我附上了一张显示仪器泄漏屏幕的屏幕截图 请尝试用 NSDictonary 替换 NSMutableDictonary 并运行工具 - 想看看分配是否会减少。

以上是关于根据仪器的iOS内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

仪器报告内存泄漏。不明白为啥

如何解决 iOS App 中的内存泄漏问题?

一分钟后webview中的内存泄漏仅通过仪器

重新仪器“内存泄漏”分析,其他工具是不是提供更多信息以找到泄漏的根本原因?

分析仪结果与仪器泄漏:iPhone 内存泄漏

UITabBarItem 内存泄漏