【问题标题】:Cocoa memory management - object going nil on meCocoa 内存管理 - 对我来说对象为零
【发布时间】:2010-06-16 07:37:27
【问题描述】:

Mac OS X 10.6,Cocoa 项目,带有保留/释放 gc

我有一个函数:

  • 遍历特定目录,扫描子文件夹(包括嵌套文件夹),构建字符串 NSMutableArray(每个找到的子文件夹路径一个字符串),然后返回该数组。

例如(为简洁起见,删除了错误处理)。

NSMutableArray * ListAllSubFoldersForFolderPath(NSString *folderPath)
{
    NSMutableArray *a = [NSMutableArray arrayWithCapacity:100];
    NSString *itemName = nil;
    NSFileManager *fm = [NSFileManager defaultManager];
    NSDirectoryEnumerator *e = [fm enumeratorAtPath:folderPath];

    while (itemName = [e nextObject]) {
        NSString *fullPath = [folderPath stringByAppendingPathComponent:itemName];
        BOOL isDirectory;
        if ([fm fileExistsAtPath:fullPath isDirectory:&isDirectory]) {
            if (isDirectory is_eq YES) {
                [a addObject: fullPath];
            }
        }           
    }
    return a;
}

调用函数在每个会话中只获取一次数组,保留它以供以后处理:

static NSMutableArray *gFolderPaths = nil;

...

gFolderPaths = ListAllSubFoldersForFolderPath(myPath);
[gFolderPaths retain];

在这个阶段一切看起来都很好。 [gFolderPaths count] 返回找到的正确路径数,[gFolderPaths description] 打印出所有正确的路径名。

问题:

当我稍后使用 gFolderPaths 时(例如,下一次运行我的事件循环)我的断言代码(以及 Xcode 中的 gdb)告诉我它是 nil。

在那次初始抓取之后,我没有以任何方式修改 gFolderPaths,所以我假设我的内存管理被搞砸了,并且 gFolderPaths 正在被运行时释放。

我的假设/假设

当我将每个字符串添加到可变数组时,我不必保留它,因为这是自动完成的,但是一旦数组从函数交给我,我就必须保留它,因为我不会立即使用它。这是正确的吗?

感谢任何帮助。

【问题讨论】:

    标签: cocoa memory-management


    【解决方案1】:

    对象不会“去nil”。

    static NSMutableArray *gFolderPaths = nil;
    

    这个声明声明gFolderPaths 是一个变量,它持有一个指向 NSMutableArray 对象的指针。 您使用指向无对象的指针对其进行初始化:nil

    这个初始化是有效的,并且是有意义的,因为你还没有一个数组可以放在这里——最好使用nil 指针进行初始化,而不是不初始化并冒一些随机指针在变量中的风险。 (static 变量不会发生这种情况,因为 static 变量无论如何都会初始化为 nil,但显式性很好,显式初始化是无害的。)

    当我稍后使用gFolderPaths 时(例如,下一次运行我的事件循环)我的断言代码(以及Xcode 中的gdb)告诉我它是nil

    在那次初始抓取之后我没有以任何方式修改gFolderPaths,所以我假设我的内存管理被搞砸了,gFolderPaths 正在被运行时释放。

    没有。运行时不释放对象。运行时是语言的一部分,retainrelease 是 Foundation 框架的一部分。该框架位于语言之上。

    因此,您可能会猜测您或其他一些代码(例如,在框架中)释放了您之前存储在 gFolderPaths 中的指针的对象。

    没有。如果发生这种情况,gFolderPaths 变量不会突然包含nil;它仍将包含指向同一对象的相同指针。如果这是对象死亡前的最后一个版本,gFolderPaths 变量仍将包含指向同一个现已死亡对象的相同指针。

    尝试记录指针(例如,使用NSLog(@"%p", gFolderPaths))将打印一个看起来有效的地址,例如0x2381ab6780。尝试记录对象(例如,使用%@)几乎肯定会崩溃,因为对象已死。

    事实并非如此。您说您的断言和对调试器的命令显示gFolderPaths 变量包含nil

    有两种明显的可能性:

    1. 重新分配给变量的东西。你说你的代码没有重新分配给变量。没有其他人应该知道它,所以这种可能性极小。
    2. 您从来没有将对象的指针分配给变量。您分配了nil,或者您从未分配过任何东西。你说你正在记录你分配给变量的指针的数组,并且描述检查出来,所以我们可以完全排除这种可能性。 (记录计数并不是一个可靠的测试,因为[nil count] 将成功返回 0。)

    这导致了第三种可能性:

    3。 您有两个 gFolderPaths 变量。

    我猜你有两个函数或方法(或各一个)都包含这一行:

    static NSMutableArray *gFolderPaths = nil;
    

    那是行不通的。 gFolderPaths 变量都是静态的,但也是您在其中声明它们的函数/方法的本地变量。每个函数/方法都有自己的 gFolderPaths 变量,因此您有两个这样的变量,彼此分开。

    您需要在任何函数或方法之外将gFolderPaths 声明为静态全局 变量。更好的是,如果它只能从实例中访问,请将其设为实例变量。无论哪种方式,如果您想在两个函数或方法之间共享它,它都不能是局部变量。

    另一种可能发生这种情况的方式是,如果您有两个这样的 global 声明,但每个声明都在不同的文件中。在文件范围内声明的变量上的static 表示“仅在此文件中可见”,因此这会导致相同的问题:当您的意思是拥有一个共享变量时,两个单独的变量。如果这是您的问题,直接的解决方法是从它们中删除 static 关键字,但如果您打算以这种方式使用全局变量,则应该重新考虑您的设计。

    【讨论】:

    • ...这只是全局变量不是个好主意的原因之一。
    • 非常感谢彼得。你用答案 #3 确定了它:我实际上已经声明了两个“gFolderPaths”,因为我复制粘贴了作业。例如“NSMutableArray *gFolderPaths =”。哎哟!这样做已经 20 多年了,但我仍然被基础知识所困扰。 @Alex,我很少使用全局变量。我在适当的地方将它们与“静态”一起使用。我 99% 的跨模块数据访问是通过一个有据可查、规范化的 API 完成的。干杯。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    • 2011-07-26
    • 1970-01-01
    • 2023-03-16
    • 2017-08-21
    • 2011-03-12
    相关资源
    最近更新 更多