【问题标题】:Objective C - autoreleasepool and ARC leak memoryObjective C - autoreleasepool 和 ARC 泄漏内存
【发布时间】:2013-05-23 04:17:42
【问题描述】:

在我的理解中,如果ARC被启用,@autoreleasepool{}里面的对象应该在它们不再使用时被释放。

但是代码

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        for (int i = 0; i < 1000000000; i++) {
            NSString *string = @"ab c";
            NSArray *array = [string componentsSeparatedByString:string];
        }
    }
    return 0;
}

内存泄漏。

仪器显示泄露的对象是由 -[NSString componentsSeparatedByString:] 创建的 __NSArrayM

那么,问题是:为什么 __NSArrayM 对象没有在创建它们的同一循环迭代中被销毁?

谁能帮我解决这个问题

更新:感谢您的回答,我似乎错误地使用了“内存泄漏”一词,并且误解了 @autoreleasepool{} 的工作原理。为了解决这个问题,我应该在 for 循环中放置 @autoreleasepool{}

【问题讨论】:

  • 这段代码分配了很多内存但没有泄漏。
  • 如果你知道这会造成泄漏,为什么还要做这么大的循环来证明这一点而不是一次迭代呢? :)
  • 我实际上认为这是一个很好的问题示例。我不知道为什么它获得了如此多的反对票。
  • @JamesWebster 因为在命令式语言中,质疑过大循环或对象的奇怪行为是荒谬的。如果这是 Haskell,那就不一样了。
  • 这个问题很好地说明了发布池的含义。通过使循环变大,它可以使问题得到更大的缓解。特别是对于来自新用户的问题,反对票似乎过多,尤其是在没有建设性批评的情况下。我看不出这个问题如何违反 SO 的任何原则。如果您认为编写一段代码很愚蠢,那这不是学习吗?

标签: objective-c automatic-ref-counting autorelease


【解决方案1】:

要了解自动释放的工作原理,请将以下代码与您的原始代码进行比较:

for (int i = 0; i < 1000000000; i++)
{
    @autoreleasepool
    {
        NSString* string = @"ab c";
        NSArray* array = [string componentsSeparatedByString:string];
    }
}

自动释放的对象在超出范围时被标记为释放,但直到自动释放部分结束时才真正释放(这是自动释放池被耗尽的地方)。因此,上面的代码将在每次循环时释放标记的对象,而您的原始代码只会在循环结束时释放它们。

自动释放池可以嵌套,当一个对象被自动释放时使用最接近的一个。这允许您从 [NSString stringWithFormat:@"%d", i]; 之类的函数返回对象 - 返回的字符串的保留计数为 1,但标记为自动释放 - 您可以暂时使用它,但如果您需要保留它以供以后使用,您需要保留它(当您将其分配给强引用时会发生这种情况)。因此,当您保留它时,保留计数变为 2,然后当它自动释放时,保留计数变为 1,一切都很好。如果你从不保留它,那么当自动释放时,保留计数变为 0,并且对象被释放。

【讨论】:

    【解决方案2】:

    据我了解,如果启用了 ARC,@autoreleasepool{} 内的对象应该在不再使用时释放。

    好的,退后一步,真正考虑一下这个问题。 ARC 并不是所有内存泄漏的神奇包罗万象,它仍然是手动保留释放,只是编译器插入手动保留释放。 @autoreleasepool {} 在给出“内存泄漏”的示例时并没有排除谨慎的必要性,也没有赋予您分配 10 亿个对象的权利并将其称为框架错误。事实证明,你所做的可能是它给你的自动释放池超载了,你要么没有等待足够长的时间让循环正确终止,要么你被操作系统杀死(我敢打赌后者多于前者)。我自己运行了这个,我可以确认给定的自动释放池确实耗尽了它分配的 Gigs RAM,它只需要很长时间。

    如果你移除循环,甚至缩短它的持续时间(100,甚至 1000 应该足够了),你可以看到绝对没有物体泄漏,并且一切都按照恐龙的方式进行:

    【讨论】:

      【解决方案3】:

      你的理解是错误的

      1. 这与 ARC 没有任何关系
      2. 不是垃圾回收,所以对象停止使用时不会被释放
      3. 自动释放池在耗尽 ([NSAutoreleasePool drain]) 或销毁时释放对象。您没有明确地排空池,因此它只会在 return 之前排空一次。

      【讨论】:

      • 如果你尝试像这样分配 10 亿个对象,即使 GC 也会有一个 conniption
      • @CodaFi 好吧,GC 可以在每次迭代后轻松释放对象。然而,更大的问题是“几乎无限”的 for 循环,它可以阻止其他线程正常工作。这真的取决于 GC 的实现。
      • OS X 垃圾收集器在低优先级后台线程 IIRC 上运行,所以我想知道在这些情况下它会如何“停止世界”(但它可能只是被拥塞循环)
      猜你喜欢
      • 1970-01-01
      • 2015-06-20
      • 2011-06-07
      • 1970-01-01
      • 2012-04-03
      • 2012-08-16
      • 1970-01-01
      相关资源
      最近更新 更多