NSArray 内存管理

Posted

技术标签:

【中文标题】NSArray 内存管理【英文标题】:NSArray Memory Management 【发布时间】:2010-09-08 14:44:29 【问题描述】:

由于某种原因,当我释放 NSArray 时,我得到了 EXC_BAD_ACCESS 异常。这是实现:

-(void) loadAllAlphabets

    NSBundle *bundle = [NSBundle mainBundle]; 
    NSArray *imagesPath = [[NSArray alloc] init]; 

    imagesPath = [bundle pathsForResourcesOfType:@"png" inDirectory:@"Images"];

    alphabets = [[NSMutableArray alloc] init]; 

    NSString *fileName = [[NSString alloc] init]; 

    for(int i=0; i<= imagesPath.count -1 ; i++) 
    
        fileName = [[imagesPath objectAtIndex:i] lastPathComponent];
        CCSprite *sprite = [CCSprite spriteWithFile:fileName];

        sprite.userData = [[fileName stringByDeletingPathExtension] uppercaseString];   

        [alphabets addObject:sprite]; 
    

    // release fileName 
    [fileName release]; 
    fileName = nil; 

    [imagesPath release]; // this causes the application to crash with EXC_BAD_ACCESS
//  imagesPath = nil; 

更新 1:

所以,问题在于,虽然我负责释放 imagesPath 对象,因为我使用了 alloc,但当 pathForResourcesOfType 返回一个自动释放对象时,它很快就变得无关紧要了。 这意味着我不应该手动释放 imagesPath 对象。

应使用以下行:

NSArray *imagesPath = [bundle pathsForResourcesOfType:@"png" inDirectory:@"Images"];

更新 2:

另一个与这篇文章相关的问题。在下面的代码中,我手动初始化了一个新的 NSMutableArray。

alphabets = [[NSMutableArray alloc] init]; 

稍后我将 CCSprite(Cocos2d 对象)插入到字母数组中。 CCSprite 是自动释放对象。我还需要手动释放字母吗?因为,一段时间后,所有对象都被释放并且内存将被返回,但是字母 NSMutable 数组中会留下什么?

【问题讨论】:

当您将 imagesPath 重新分配给 pathsForResourcesOfType: 调用时会发生泄漏,因为您已经分配了并且 i 【参考方案1】:

我认为混乱就在这里:

NSArray *imagesPath = [[NSArray alloc] init]; 
imagesPath = [bundle pathsForResourcesOfType:@"png" inDirectory:@"Images"];

第一行创建一个新对象。这个对象确实需要释放。

第二行用一个新的、自我管理的对象覆盖该对象。这需要手动释放。

这意味着您正在泄漏第一个 imagesPath。

一般情况下,如果你alloccopy 它,你需要释放一个对象。并且您不应该在释放(或自动释放)其内容之前覆盖对象。

【讨论】:

【参考方案2】:

内存管理中的一般经验法则 - 只有在使用包含 new、copy 或 alloc 的方法获取对象时才应该释放对象(标准方法遵循该规则,您也应该遵守该规则)。

在您的情况下,您使用 pathsForResourcesOfType: 方法获取 imagesPath 对象,该方法返回一个自动释放的对象,因此您不能自己释放它。

编辑: 是的,您需要在某处释放alphabets 对象(出于同样的原因 - 您使用 alloc 方法得到了它)。

Objective-c 容器拥有添加到它们的对象的所有权,当我们将对象添加到数组时,它们会被保留,因此可以保证它们的生命周期至少与容器的生命周期一样长。当您从集合中删除对象或集合本身被销毁时,其成员将被释放(以补偿添加时的保留)。

【讨论】:

谢谢弗拉基米尔!因此,即使我分配了 imagesPath,pathsForResourcesOfType 也返回了一个我不能手动释放的自动释放对象。【参考方案3】:

此外,当您使用空的非可变数组初始化 imagesPath 时,您正在泄漏内存,然后当您将 pathsForResources: 的结果分配给它时将其丢弃。只需这样做:

NSArray *imagesPath = [bundle pathsForResourcesOfType:@"png" inDirectory:@"Images"];

fileName 出现同样的错误。不需要用一个空的不可变字符串来初始化它。

也不要释放fileName,因为它也是一个自动释放的对象。

【讨论】:

fileName 是 autorelease 对象的原因是它来自于 imagesPath NSArray 对吧? 另外,如果我正在运行一个循环并分配 fileName 是否意味着我正在为每个循环计数创建新的 NSString 对象。 @azamsharp 是的,'fileName = [[NSString alloc] init];' 行中的值当您在循环内为其分配自动释放的字符串时,将被丢弃。就个人而言,我会在循环之前用 nil 初始化 fileName 是的,您在每次循环迭代时创建一个字符串对象,但这些字符串是自动释放的,因此运行时会自行处理它们 - 您无需担心它们 我刚刚在循环外创建了一个 NSString *fileName = nil。

以上是关于NSArray 内存管理的主要内容,如果未能解决你的问题,请参考以下文章

iOS内存管理--NSArray与NSMutableArray用copy修饰还是strong

关于 iPhone 中的内存管理

目标 C:块案例中的内存管理

了解 objc 中块内存管理的一种极端情况

根据仪器的iOS内存泄漏

OC基础 NSArray 容器