4

在我正在开发的应用程序中,我正在 for 循环中从文本文件中读取数值,然后进行一些计算并将结果附加到结果字符串中。

该文件中有 22050 个值。我注意到,超过一定数量的循环/附加值(~5300)它往往会崩溃。

我想也许我有内存泄漏,所以我摆脱了附加的字符串,一切正常。我试图摆脱所有东西,但附加的字符串和应用程序崩溃了。我对所有异常都有一个断点,我没有得到任何异常。

我想确定一下,所以我开始了一个新项目。我只放了一个 UIButton ,当它被推送时会调用这段代码:

- (IBAction)TestPressed:(id)sender
{
    NSString *testString = @"";

    for (int i = 0; i < 22050; i++)
    {
        testString = [testString stringByAppendingString:@"12.34567890\n"];
    }

    NSLog(@"%@", testString);
}

我在 NSLog 行上有一个断点。该应用程序之前崩溃。

NSString 长度有限制吗?会不会占用太多内存?

4

3 回答 3

13

问题是您在每次迭代中都创建了一个新字符串。有两个选项可以解决这个问题: 使用可变字符串来创建结果:

NSMutableString *testString = [NSMutableString string];

for (int i = 0; i < 22050; i++)
{
    [testString appendString:@"12.34567890\n"];
}

NSLog(@"%@", testString);

...或使用自动释放池删除循环中的实例:

NSString *testString = @"";

for (int i = 0; i < 22050; i++)
{
    @autoreleasepool {
        testString = [testString stringByAppendingString:@"12.34567890\n"];
    }
}

NSLog(@"%@", testString);

请注意,我包含第二个版本只是为了说明问题发生的原因以及如何解决它。它仍然是低效的,因为它创建了 22049 个平均长度为 120,000 个字符的临时字符串。

于 2013-03-18T10:38:00.983 回答
2

用于NSMutableString附加字符串,否则会分配太多内存。

于 2013-03-18T10:37:21.977 回答
0

+[NSString stringByAppendingString:]每次调用它时都会创建一个新的、自动释放的对象。在自动释放池弹出之前,自动释放的对象不会被释放,这通常意味着在事件循环结束时。在这种情况下,最好使用 anNSMutableString作为其他答案的建议。但是,如果您确实遇到了需要创建许多自动释放对象而不是使用可变对象的问题,您可能需要在@autoreleasepool {}创建对象的代码周围包裹一个块,这样您就不会出现大量内存膨胀,但是您必须小心保留要在@autoreleasepool块范围之外停留的对象。

一般来说,我尽量避免使用自动释放的对象,原因如下:

1) 对象实际被释放的时间可能是不确定的,除非您的方法本身中确实有一个自动释放池。否则,无法知道自动释放池的范围以及调用代码何时决定弹出池。

2) 自动发布本质上比手动发布更占用资源。对象需要添加到池中,然后迭代并稍后释放。

3) 自动释放在多线程环境中变得特别棘手,因为您经常会在释放对象的线程之间发生竞争。在这些情况下,很容易创建一个具有难以重现或找到根本原因的间歇性崩溃的场景。

4) 在处理自动释放时,崩溃通常更难分析。您经常会看到堆栈跟踪只是一个自动释放池弹出一个对象,如果没有重现案例,几乎不可能确定它是什么对象,释放它的代码路径等。

5) 有时,像重构这样简单的事情会以您通常意想不到的方式影响自动释放的对象。从简单迭代更改为基于块的迭代引入了一个自动释放池,它可能会在您有机会保留对象之前释放它。

通常,自动释放对象最合适的用途是动态创建并由非构造方法返回的对象。这样调用代码就有机会保留对象,但被调用的方法不必担心自己指定所有者。

这有点啰嗦,也许超出了原始问题的范围,但我认为了解内存管理中良好决策背后的“原因”很重要。

于 2013-03-18T10:59:21.703 回答