6

前言; 这不是一个普遍的“我有一个有泄漏的巨型应用程序”的问题。这是关于自动引用计数在具有完整源代码的几乎微不足道的演示应用程序中无法正常工作的特定问题,或者是微妙的代码生成或编译器问题,或者是 Instruments 中的错误。(TLDR:哦。实际上是一个奇怪的小比赛条件)

我对 Instruments 的“分配”列表显示实例泄漏这一事实感到困惑,然而,我有该类的一个实例,只有一个,而 ARC 导致调用 dealloc 方法,我知道它被调用是因为当 dealloc 完成时,会打印一条 NSLog 消息,但它仍然显示在 Instruments 的泄漏列表中。

retainCount 永远不会超过 1。它没有被任何人保留,它正在被释放,但它看起来像是一个“泄漏”,因为它在 Instrument 的泄漏中显示为一个活动实例。

这怎么可能?

我还在用 ARC 学习 Objective-C,所以我想我一定犯了一个常见的初学者错误。这是我唯一的 init 和我的对象的 dealloc:

- (id) initWithMessage:(NSString*)messageForUser
{
    self = [super init];
    if (self)
    {
        _message = messageForUser;
        NSLog( @"from constructor: %@",_message);
    }
    return self;

}

- (void)dealloc {
    NSLog(@"Goodbye cruel world. One WPMyObject signing off.");
   // [message release]; // ARC forbiddeth thee! Begone release.
    _message = nil;

   // [super dealloc]; // ARC forbiddeth explicit super dealloc
}

只是想看看我是否可以,我尝试调用[super dealloc]dealloc 方法,然后 ARC 以一个错误阻止你,这很好,因为它会为你做这件事。但是,当我编写自己的 init 方法时,它不会阻止我。

当我在 XCode 中使用“Run”运行程序时,我得到了“再见残酷世界”NSLog 消息,正如我希望的那样,当运行 dealloc 时,我也得到了这个实例仍然存在于末尾的证据运行,当使用内存泄漏模板进行仪器分析时:

如果我在没有下面的实例创建代码的情况下运行,我只会报告一些标准库 malloc 泄漏,但如果我添加此代码,则会发生所有泄漏:

  WPMyObject * myObject = [[WPMyObject alloc] initWithMessage: @"Hello World!\n" ];

这是我所看到的,我认为我理解的是告诉我 WPMyObject 的唯一实例正在泄漏:

在此处输入图像描述

非常简单的完整源代码(使用 Objective-C 和 的小型 Mac OS X 命令行应用程序Foundation/Foundation.h)在 BitBucket 上。单击此链接可在浏览器中查看源代码。

@autoreleasepool {...}main.m 单元在上下文语句中运行单个测试对象实例创建:

在此处输入图像描述

如果您想在自己的计算机上查看完全微不足道的代码,请像这样抓取它:

  hg clone https://bitbucket.org/wpostma/objectivecplaymac

}更新:您可以通过在结束自动释放池之前添加这行代码来修复“泄漏”(可能是 Instruments 中的错误,或 clang/llvm 编译器错误,而不是“真正的泄漏”) :

  NSLog( @"Reached end of autorelease pool" );

是的。添加日志消息。“泄漏”消失。这是 Mac OS X 10.7.5 上的 XCode 4.5.2 (4G2008a),包含 Instruments 版本 4.5 (build 4523)。

Update2:这确实是一个简单的多进程竞争条件。下面的代码似乎是一个合理的调试构建位黑客。如果有更明确的说法“等到 Instruments 用我完成,然后退出 main()”,那可能是一个不错的未来功能,Apple 工程师。PROFILER_SYNC("MESSAGE") 宏在发布模式下扩展为空,但在调试版本中,将“MESSAGE”发送到分析器......确实非常方便。

int main(int argc, const char * argv[])
{
    // This creates and cleans up an auto-release pool context.
    @autoreleasepool {

        foo(); // Microscopic amounts of debug code you want profiler to analyze go here.

#ifdef DEBUG
        usleep(10000); // race condition prevention in debug builds, so Instruments can finish up.
#endif

    }
#ifdef DEBUG
    usleep(10000); // race condition prevention in debug builds, so Instruments can finish up.
#endif

   return 0;
}
4

1 回答 1

3

听起来不像是泄漏,更像是比赛条件。NSLog编写和程序执行之间的竞争被终止。或者缓冲问题,更有可能是在达到某个阈值之前不会刷新输出。

尝试sleep(100);在自动释放池的右大括号之后放置一个。或者尝试在dealloc.

如果这不能“修复”它,则显示反汇编并查看两个版本的代码之间的变化。


发生这种情况的原因: 出于性能原因, Instruments 和 NSLog() 都被有效缓冲。它们被设计用于运行时间相对较长的进程,该进程几乎总是有一个主事件循环或调用dispatch_main().

这样做是为了尽量减少对目标应用程序性能的影响,但它可能会导致微基准测试出现异常,因为该过程的生命周期非常短暂。

如果您在短暂的过程中有一个最小的测试用例,我建议您关闭main()with [[NSRunLoop currentLoop] run];。这将永远运行,并为仪器和/或调试器提供一个可行的运行时。

于 2013-01-11T17:13:53.993 回答