清理App全球资源的最佳场所?

时间:2012-08-25 11:50:43

标签: objective-c macos nsdocument nsapplication-delegate

停止播放确定在以下问题中看到retainCount一词之前,请跳至底部的编辑,我已声明我有停止使用它。

我的Cocoa App使用MRR创建了许多全局资源,我在调用main()之前加载NSApplicationMain()。由于NSApplicationMain()没有返回,我使用atexit()隐藏了这些资源的清理,如下所示:

atexit(cleanup);

if (![CocoaUtil initCocoaUtil] ||
    ![PreferenceController initPreferenceController] ||
    ![ResourceManager initResourceManager])
{
    criticalAlertPanel(@"Failed to initialize application",
                       @"Failed to initialize application");
    return 4;
}

retval = NSApplicationMain(argc, (const char **)argv);

然而cleanup()NSDocument子类中的任何视图dealloc'之前被调用(我没有显示此消息的日志消息),因此参考计数为全局资源中的对象有时为> 1。我过于谨慎,并试图通过使用此方法释放我的全局资源来预防内存泄漏:

+ (void)fullRelease:(id)obj
             format:(NSString *)format, ...
{
    if (obj == nil)
        return;

    NSUInteger retainCount = [obj retainCount];
    if (retainCount > 1)
    {
        va_list va;
        va_start(va, format);
        NSString *objDesc = [[NSString alloc] initWithFormat:format arguments:va];
        logwrn(@"%@ has a reference count of %lu", objDesc, retainCount);
        [objDesc release];
    }

    while (retainCount > 0)
    {
        [obj release];
        retainCount--;
    }
}

我的日志显示以下内容:

12:15:04.954 INF -[AppController applicationDidFinishLaunching:] Application launched
12:15:06.702 INF -[AppController applicationShouldTerminate:] Application terminating
12:15:06.703 INF -[AppController applicationWillTerminate:] Application terminating
12:15:06.705 DBG cleanup Cleaning-up
12:15:06.705 INF +[ResourceManager finiResourceManager] Cleaning up
12:15:06.709 WRN +[CocoaUtil fullRelease:format:] _images[2] has a reference count of 2
12:15:06.709 WRN +[CocoaUtil fullRelease:format:] _images[3] has a reference count of 2
12:15:06.709 WRN +[CocoaUtil fullRelease:format:] _images[4] has a reference count of 2
12:15:06.710 WRN +[CocoaUtil fullRelease:format:] _images[5] has a reference count of 2
12:15:06.710 WRN +[CocoaUtil fullRelease:format:] _images[6] has a reference count of 2
12:15:06.710 WRN +[CocoaUtil fullRelease:format:] _images[7] has a reference count of 2
12:15:06.711 WRN +[CocoaUtil fullRelease:format:] _images[8] has a reference count of 2
12:15:06.711 WRN +[CocoaUtil fullRelease:format:] _images[9] has a reference count of 2
12:15:06.721 DBG +[PreferenceController finiPreferenceController] Cleaning up
12:15:06.721 DBG +[CocoaUtil finiCocoaUtil] Cleaning up

我的问题(终于!)是:

有没有办法确保在所有NSDocument实例被销毁并且停止获取这些错误否定后清理我的全局资源?

编辑:我取消了fullRelease调用,只是在我的资源上执行了正常的release,而且仪器没有检测到任何内存泄漏,所以一切正常,但是我很好奇为什么NSDocument对象在调用atexit()之前似乎没有被释放。

4 个答案:

答案 0 :(得分:7)

不要发布您不拥有的内容!

每个保留属于其他人。仅向release发送new以平衡对alloccopyretain-release(NARC。)的调用。此类行为将不可避免地导致生产代码崩溃。

看起来你想要确保一个对象被解除分配而不是简单地处理。如果您拥有该对象,请释放一次;对它的其他引用属于其他对象。您可能代码中存在内存泄漏(我们无法从此代码示例中得知)但通常可以使用静态分析器,仪器和一些肘部油脂找到这些内容。< / p>

更重要的是:当您的进程退出时,操作系统将为您释放所有内存。这不是C标准的一部分,但它只是OS X和iOS的运行方式,而且它是支持Objective-C的其他平台上的预期行为。因此,除了将文件写入磁盘或类似文件之外,您不必在进程退出时执行任何特殊操作。事实上,许多Cocoa应用程序都不愿意发布其应用程序委托所拥有的任何内容,因为让操作系统转储内存比在数千个上调用-retainCount更快。对象。

请勿致电-retainCount

它的谎言。干净利落。它包括Cocoa使用的临时引用,最重要的是,你应该从不试图干扰那些。 {{1}}是一个有毒的符号。

答案 1 :(得分:7)

一些注意事项:

  • 不要使用retainCount;有关详细信息,请参阅http://whentouseretaincount.com上的链接

  • atexit()处理程序在高级编程中无用。调用时,应用程序处于相对未定义的状态。框架将拆除一些东西,但正如你所指出的,将会有大量的对象永远不会被释放。在某些情况下,可能根本不会调用atexit()。

  • 您不能依赖应用程序终止来执行任何类型的必需状态清理。用户可能会强行退出您的应用。系统也可能或者强制重启。应将终止行为视为优化;你可以做一些能让下一次发射更快的东西。

  • 在应用终止期间,无需解除分配任何内容。无论应用程序状态如何,系统都会在终止时回收所有应用程序资源。换句话说,终止时留在内存中的对象不会泄漏

  • 一般来说,“泄漏”检测工具只能用于解决明显的问题。泄漏仅检测内存泄漏。它无法检测内存增加,其中对象累积仍然以某种方式连接到全局。吸收不是技术上的泄漏,但它们很容易成为问题的重要来源。

  • HeapShot analysis会检测到泄漏和内存增加。

  • 考虑迁移您的应用以使用ARC。 ARC的指导模式之一是编译器完全了解对象的生命周期。 ARC下不允许表达模糊不清的表达(在某些情况下没有适当的标记)。同样,ARC编译器和分析器可以对您的代码进行更深入的分析,发现许多可能非常微妙的问题。

答案 2 :(得分:3)

禁用突然终止,不使用全局变量,只使用常规引用计数规则。在某些情况下,您需要打破强循环引用或手动清除对象的实例变量。最后,在主要返回之前暂停/中断可能更有帮助,并在此阶段运行heap以查看真正存在的(泄漏)。

  

我过于谨慎,并试图通过使用此方法释放我的全局资源来抢占内存泄漏。

你不应该这样清洗 - 你知道的更好:)

但是,找到并销毁您已控制的所有引用/对象/分配实际上是一种非常好的做法,以确保您的程序运行良好,可重复使用,以及作为应用程序更改时监控回归的良好指标。

答案 3 :(得分:2)

正如所有其他人所说:只发布 你拥有的东西。你的应用程序现在可能不会崩溃,但你很幸运。当你这样做,或者在两个小时内,或者其他什么时,它可能会在另一台Mac上崩溃。

如果某个对象持有的稀缺资源确实需要取消注册(例如,网络会话到另一台服务器,如果没有正确发送它,则会发出签名消息导致服务器等待你,为你的返回保留一些内部状态),你应该将那个构建到你的对象中,而不是在你的对象被释放时这样做。

E.g。在应用程序退出NSApplicationWillTerminate之前,会有Mac OS X发送的通知。让你的对象注册它,当它得到它时,让它向服务器发送它的再见(并记住它已经做到了)。这样,当您的应用程序退出时,例如应用程序委托仍然坚持使用该对象,您仍然确定签名将会发生。

但你可能还应该检查你是否已经在-dealloc中注销了。有一天,您可能会添加对多个服务器的支持,然后当您释放一台服务器时,即使您的应用程序不会退出,也希望它发送一个再见。但是如果你还没有这样做的话就会签名。

同样适用于你在退出之前要记录的内存中的一些数据(例如NSUserDefaults确保对首选项的访问速度很快,但在你退出之前仍然会写入磁盘)。< / p>

但像这样的案件真的很少见。通常,NSDocument会在需要时调用您写入磁盘,通常服务器会在连接断开并自行清理时发出通知。所以你真的不应该发布不属于你的东西。对于某些用户,您只会导致退出崩溃。它会给人留下非常糟糕的印象,并且会导致您的应用在之前实际失败它有机会保存一些数据,因此会让事情变得更糟。