Objective c 目标C/iOS:使用ARC释放内存(内存泄漏)

Objective c 目标C/iOS:使用ARC释放内存(内存泄漏),objective-c,ios,memory-leaks,automatic-ref-counting,Objective C,Ios,Memory Leaks,Automatic Ref Counting,我不熟悉iOS/Objective-C,我不能正确理解内存的释放。 为了测试它,我创建了一个空的ARC启用的iPhone项目,并创建了一个非常简单的测试类: #import "MemTest.h" @implementation MemTest { } -(void) start { for (int i = 0; i < 1500000; i++) { NSMutableString *myString = [NSMutableString string];

我不熟悉iOS/Objective-C,我不能正确理解内存的释放。 为了测试它,我创建了一个空的
ARC
启用的iPhone项目,并创建了一个非常简单的测试类:

#import "MemTest.h"

@implementation MemTest {

}

-(void) start {
    for (int i = 0; i < 1500000; i++) {
        NSMutableString *myString = [NSMutableString string];

        // The appended string is 2000 characters long in the real test class.
        [myString appendString:@"12345678901234567890123456 <very long>"];

        if (i % 1000 == 0) {
            NSLog(@"i = %d", i);
        }

        myString = nil;
    }
}

@end
应用程序(如预期)打印了许多漂亮的数字“i=xy”,但内存使用量随着每次迭代而增加,最后应用程序崩溃:

....
2012-12-06 20:17:40.193 MemTestApp[19250:11303] i = 930000
2012-12-06 20:17:40.208 MemTestApp[19250:11303] i = 931000
MemTestApp(19250,0xac63f2c0) malloc: *** mmap(size=16777216) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
所以我的问题是:为什么内存使用量在增长


我认为通过分配nil,当使用
ARC
时,应该释放内存。我在这里遗漏了什么?

一些事情可能会出错:

  • 您可能实际上没有启用ARC。你应该仔细检查一下。最简单的方法是在代码中加入
    -保留
    ,并确保抛出编译器错误

  • ARC不一定会阻止对象进入自动释放池。如果可以的话,它试图抓住这一点,但它没有做出保证。值得注意的是,在
    -O0
    (无优化)时,它通常不会阻止对象进入自动释放池。这很可能就是你正在发生的事情

  • 即使在更高的优化级别,启用ARC的代码仍然不能保证捕获自动释放

  • 如果将
    @autoreleasepool{}
    粘贴在
    for
    循环中,您会发现内存使用应该消失。或者,您可以尝试使用
    [NSMutableString]
    ,而不是使用
    [NSMutableString new]
    ,它根本不使用自动释放池*但在ARC代码中的行为应该相同

    *嗯,
    NSMutableString
    可以自由地在内部使用autorelease池

    所以我的问题是:为什么内存使用量在增长

    因为所有的分配都是在一个循环中进行的。所有这些字符串都是自动释放的对象,当顶部的自动释放池被清空时,它们就会被清除,这在每次运行循环中都会发生。但您根本没有给运行循环一个运行的机会,因此自动释放池永远不会耗尽,内存也不会耗尽


    ARC使您不用担心管理单个对象,但您仍然需要了解,因为ARC根据相同的规则工作。

    方法返回一个“autoreleased”对象。这意味着对象被放入“自动释放”池。当池被耗尽时,对象将被释放(并且,如果不再有对它的强引用,它将被释放)。自动释放池在运行循环结束时自动排空(就在系统进入睡眠等待另一个事件之前)

    在编写可能分配大量自动释放对象的循环时,可能需要管理自己的自动释放池:

    -(void) start {
        for (int i = 0; i < 1500000; i++) {
            @autoreleasepool {
                NSMutableString *myString = [NSMutableString string];
                ...
            }
        }
    }
    
    -(无效)开始{
    对于(int i=0;i<1500000;i++){
    @自动释放池{
    NSMutableString*myString=[NSMutableString];
    ...
    }
    }
    }
    
    该代码在每个循环迭代开始时创建一个新的自动释放池,并在每个循环迭代结束时将其耗尽。因此,您创建的每个字符串都将在循环结束时释放,并且在您的示例代码中,由于没有其他内容保留它,因此将被释放


    有关更多信息,请阅读,特别是“使用自动释放池块”一章。

    我不认为自动释放的对象会以不同的方式释放,只是因为您的项目具有ARC。实际上,autorelease池的工作方式与此完全相同:因为您在循环中分配了大量对象,所以在迭代时池永远不会耗尽

    您应该尝试直接在循环体内部强制一个新的
    自动释放池
    ,看看这是否解决了您的问题。当然,这可能有些过分,您可以尝试将循环拆分为两个嵌套的循环,以便不时拥有一个自动释放池,例如

    for (int i = 0; i < TOTAL_STEPS; ++i) {
      @autoreleasepool {
        for (int j = 0; j < STEP_SIZE; ++j) {
          ..
        }
      }
    }
    
    for(int i=0;i

    我甚至不认为将局部变量设置为
    nil
    会对您的情况产生影响,因为它是循环体范围的局部变量,编译器已经知道您不能在其他任何地方使用它。

    谢谢您的回答!他们把事情弄清楚了。在循环中使用自动释放池实现了这一点。但是我从你的回答中得到的主要信息是:我必须阅读更多关于内存管理的内容另一件需要注意的事情是,僵尸通常在方案中打开:诊断,这意味着在释放对象时,实际上不会删除任何内存。这将使自动释放池看起来也会泄漏。在这种情况下,我该怎么办
    for (int i = 0; i < TOTAL_STEPS; ++i) {
      @autoreleasepool {
        for (int j = 0; j < STEP_SIZE; ++j) {
          ..
        }
      }
    }