在我正在处理的应用程序中,我正在从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长度是否有限制?它是否使用了太多内存?
答案 0 :(得分: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个字符。
答案 1 :(得分:2)
使用NSMutableString
追加字符串,否则会分配太多内存。
答案 2 :(得分:0)
+[NSString stringByAppendingString:]
都会创建一个新的自动释放对象。在自动释放池弹出之前,自动释放的对象不会被释放,这通常意味着在事件循环结束时。在这种情况下,最好使用NSMutableString
代替其他答案建议。但是如果您遇到需要创建许多自动释放对象而不是使用可变对象的问题,您可能希望在创建对象的代码周围包裹@autoreleasepool {}
块,这样就不会有一个巨大的内存膨胀,但你必须小心保留你想要留在@autoreleasepool
块范围之外的对象。
一般情况下,我会尽量避免使用自动释放的对象,原因如下:
1)当实际获得解除分配的对象时,除非您的方法本身实际上有自动释放池,否则它可能是非确定性的。否则,无法知道自动释放池的范围以及调用代码何时决定弹出池。
2)自动释放本质上比手动释放更加耗费资源。需要将对象添加到池中,然后迭代并稍后释放。
3)自动释放在多线程环境中变得特别棘手,因为你经常会在释放对象的线程之间结束比赛。在这些情况下,很容易创建一个间歇性崩溃的场景,难以重现或找到根本原因。 4)在处理自动释放时,崩溃通常难以分析。您经常会看到堆栈跟踪只是一个自动释放池弹出一个对象,没有repro情况,几乎不可能确定它是什么对象,释放它的代码路径等等。5)有时,像重构这样简单的事情会以你通常不会想到的方式影响自动释放的对象。从简单迭代到基于块的迭代的转换引入了一个自动释放池,可能会在您有机会保留它之前释放该对象。
通常,自动释放对象的最合适用途是即时创建并由非构造方法返回的对象。这样调用代码就有机会保留对象,但被调用的方法不必担心指定所有者本身。
这有点长篇大论,可能超出原始问题的范围,但我认为了解“为什么”在内存管理方面的良好决策背后很重要。