7

我是新手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

4 回答 4

7

一些事情可能会出错:

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

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

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

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

* 好吧,NSMutableString如果需要,可以在内部免费使用自动释放池

于 2012-12-06T19:28:50.517 回答
4

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

因为你在一个循环中完成了所有的分配。所有这些字符串都是自动释放的对象,一旦顶部的自动释放池被耗尽,它们就会被清理掉,每次运行循环都会发生这种情况。但是您根本没有给运行循环运行的机会,因此自动释放池永远不会耗尽并且您的内存不足。

ARC 让您不必担心管理单个对象,但您仍然需要了解Objective-C 中的内存管理是如何工作的,因为 ARC 根据相同的规则工作。

于 2012-12-06T19:29:09.503 回答
1

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

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

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

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

有关更多信息,请阅读高级内存管理编程指南,尤其是“使用自动释放池块”一章。

于 2012-12-06T19:31:22.903 回答
1

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

You should try by forcing an new autoreleasepool directly inside the loop body to see if this fixes your problem. Of course this could be overkill, you could try by splitting the loop in two nested loops to have just an autorelease pool from time to time, eg

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

I don't even think that setting a local variable to nil could make the difference in your situation, since it's a variable local to the scope of the loop body, the compiler already knows you couldn't use it anywhere else.

于 2012-12-06T19:31:27.480 回答