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

时间:2012-12-06 19:24:46

标签: objective-c ios memory-leaks automatic-ref-counting

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

#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

我只是在AppDelegate

中开始测试
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    MemTest *test = [[MemTest alloc] init];
    [test start];

    ....
}

应用程序(正如预期的那样)打印许多不错的数字“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时释放内存。我在这里缺少什么?

4 个答案:

答案 0 :(得分:7)

有些事情可能会出错:

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

  2. ARC不一定会阻止对象进入自动释放池。它试图抓住它,如果它可以,但它不保证。值得注意的是,在-O0(无优化)时,它经常不会阻止对象进入自动释放池。这很有可能发生在你身上。

  3. 即使在更高的优化级别,仍然无法保证启用ARC的代码能够捕获自动释放。

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

    *嗯,NSMutableString如果想要

    ,可以在内部自由使用自动释放池

答案 1 :(得分:4)

  

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

因为您在单个循环中进行了所有分配。所有这些字符串都是自动释放的对象,一旦顶部自动释放池耗尽,它们就会被清除,每次通过运行循环时都会发生这种情况。但是你没有给运行循环一个运行的机会,所以自动释放池永远不会耗尽,你的内存耗尽。

ARC使您免于担心管理单个对象,但您仍然需要了解how memory management in Objective-C works,因为ARC的工作原理与此相同。

答案 2 :(得分:1)

[NSMutableString string]方法返回“自动释放”对象。这意味着该对象被放入“自动释放”池中。当池被耗尽时,对象将被释放(如果没有更强的引用,它将被释放)。自动释放池在运行循环结束时自动耗尽(就在系统进入休眠状态之前等待另一个事件)。

当您编写可能分配大量自动释放对象的循环时,您可能希望管理自己的自动释放池:

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

该代码在每次循环迭代开始时创建一个新的自动释放池,并在每次循环迭代结束时将其排出。因此,您创建的每个字符串都将在循环结束时释放,并且在您的示例代码中,将被释放,因为没有其他字符串保留它。

有关详细信息,请阅读Advanced Memory Management Programming Guide,尤其是“使用自动释放池块”一章。

答案 3 :(得分:1)

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

你应该尝试直接在循环体内强制一个新的autoreleasepool,看看这是否能解决你的问题。当然这可能是矫枉过正,您可以尝试通过在两个嵌套循环中拆分循环来不时只有一个自动释放池,例如

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

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