如何修复长NSString崩溃



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

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

我想也许我有内存泄漏,所以我去掉了字符串附加,一切都工作得很好。我试图摆脱一切,但字符串追加和应用程序崩溃。我有一个断点在所有的异常,我没有得到任何异常。

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

- (IBAction)TestPressed:(id)sender
{
    NSString *testString = @"";
    for (int i = 0; i < 22050; i++)
    {
        testString = [testString stringByAppendingString:@"12.34567890n"];
    }
    NSLog(@"%@", testString);
}

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

NSString的长度有限制吗?它是否占用了太多的内存?

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

NSMutableString *testString = [NSMutableString string];
for (int i = 0; i < 22050; i++)
{
    [testString appendString:@"12.34567890n"];
}
NSLog(@"%@", testString);

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

NSString *testString = @"";
for (int i = 0; i < 22050; i++)
{
    @autoreleasepool {
        testString = [testString stringByAppendingString:@"12.34567890n"];
    }
}
NSLog(@"%@", testString);

请注意,我包含第二个版本只是为了演示为什么问题首先发生以及如何修复它。它仍然是低效的,因为它创建了22049个临时字符串,平均长度为120,000个字符。

使用NSMutableString来附加字符串,否则会占用太多内存

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

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

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

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

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

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

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

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

这有点冗长,可能超出了最初问题的范围,但我认为了解"为什么"在内存管理中做出好的决策是很重要的。

最新更新