2013-03-18 41 views
4

在我正在工作的應用程序中,我正在從for循環中的文本文件中讀取數值,然後執行一些計算並將結果附加到結果字符串中。如何修復長時間的NSString崩潰?

該文件中有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

這會讓你的差異使用可變字符串?'NSMutableString * testString = [NSMutableString string];''和'[testString appendString:@「12.34567890 | n」];' – Fogmeister 2013-03-18 10:37:15

+0

您是使用iOS還是MacOS?如果您使用的是iOS, stringByAppendingString'並且你沒有使用ARC,那麼你的內存就像一個篩子,如果你使用的是ARC,那麼至少它應該釋放舊的字符串以供新的使用@Fogmeister的建議也應該改進,不管ARC。 – gaige 2013-03-18 10:39:37

+0

@ gaige Ther在這種情況下,Mac/iOS和ARC/MRC之間沒有區別。臨時字符串只是去autorelease池。 – 2013-03-18 10:41:18

回答

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個字符的臨時字符串。

+0

+1我喜歡你的第二種方式:) – 2013-03-18 10:40:32

+1

@AnoopVaidya我更喜歡第一個。它可能要快得多。 – 2013-03-18 10:42:09

+0

是的,我總是使用那個,從來沒有嘗試過第二個,儘管知道這種方式也是可能的。 – 2013-03-18 10:47:17

2

使用NSMutableString來追加字符串,否則會分配太多的內存。

0

+[NSString stringByAppendingString:]每次調用它時都會創建一個新的自動釋放對象。自動釋放的對象不會被釋放,直到autorelease池彈出,這通常意味着在事件循環結束時。在這種情況下,最好使用NSMutableString來代替其他答案。但是,如果遇到了需要創建許多自動發佈的對象而不是使用可變對象的問題,則可能需要在創建對象的代碼周圍包裝@autoreleasepool {}塊,以便您沒有大量內存膨脹,但是你必須小心保留你想要在@autoreleasepool塊範圍之外的物體。

在一般情況下,我儘量避免使用自動釋放的對象儘可能的幾個原因:

1)當真正得到釋放可以是非確定性的對象,除非你確實有一個自動釋放池在你的方法本身。否則,無法知道autorelease池的範圍,以及調用代碼何時決定彈出池。

2)自動釋放本質上比手動釋放需要更多的資源。需要將對象添加到池中,然後迭代並稍後發佈。

3)自動釋放在多線程環境中變得特別棘手,因爲在釋放對象的線程之間通常會結束比賽。在這些情況下,創建一個間歇性崩潰的方案很容易,難以重現或找到根本原因。

4)處理自動釋放時崩潰通常難以分析。你經常會看到堆棧跟蹤只是一個自動釋放池彈出一個對象,沒有repro情況下幾乎不可能確定它是什麼對象,什麼代碼路徑將它釋放等。

5)有時,這很簡單,因爲重構可能會影響自動發佈的對象,而這些方式可能不是您通常所期望的。從簡單的迭代變爲基於塊的迭代引入了一個自動釋放池,在你有機會保留它之前可能會釋放該對象。

通常,自動釋放對象的最合適的用法是動態創建並由非構造函數方法返回的對象。調用代碼有機會保留該對象,但被調用的方法不必擔心指定所有者本身。

這是種長篇大論,或許超出了原來問題的範圍,但我想知道後面的內存管理好決策「爲什麼」是很重要的。