2010-12-11 20 views
3

經過多年的Java開發,我開始在Objective-C中編程。我正在苦苦掙扎的一個問題是內存管理。特別是,書籍和在線的大多數例子似乎都沒有考慮到由於例外而造成的內存泄漏。例如,考慮下面的方法:我應該將[pool drain]放在@finally子句中嗎?

-(void) doSomething 
{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init]; 

    // Allocate some autoreleased objects here 

    NSString *data = [NSString [email protected]"Hello"]; 

    // Do some work, exception could be thrown 

    [PotentialExeptionThrower maybeThrowException]; 

    // Clean up autorelease objects 

    [pool drain]; 
} 

不應上述改寫爲防止任何潛在的內存泄漏?

-(void) doSomething 
{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init]; 

    @try { 

     // Allocate some autoreleased objects here 

     NSString *data = [NSString [email protected]"Hello"]; 

     // Do some work, exception could be thrown 

     [PotentialExeptionThrower maybeThrowException]; 

    } @finally { 
     // Clean up autorelease objects 

     [pool drain]; 
    } 
} 

由於@ try- @ catch,上面的代碼效率低嗎?

謝謝!

回答

1

不,你不應該。我知道你的經歷受到多年Java的影響,你需要改變你處理異常的方式。

正如名稱所示,例外情況不屬於Cocoa應用程序中正常程序流的一部分。例外情況用於捕獲開發者發現的錯誤,YOU,並且在您的應用程序發貨之前應該照顧之前。用單元測試覆蓋你的數據模型,以排除其中的每一個。

例如NSInvalidArgumentException是不應該潛入生產代碼的東西。使用單元測試驗證用戶輸入並覆蓋邏輯的邊緣情況!

對於並非您的錯誤的錯誤,您可以從中恢復,而應該使用NSError實例。他們具有適合最終用戶的本地化描述的優良特性。 NSError也具有處理合適恢復選項的功能,例如「取消」「重試」

我已經寫上異常和錯誤更長的博客,帖子在iOS上的位置:http://blog.jayway.com/2010/10/13/exceptions-and-errors-on-ios/

+0

感謝您的鏈接,我發現你的博客文章非常有幫助。你對Java背景是正確的。異常處理是Java編程中不可分割的一部分,我用@try @catch @finally填充我的Objective-C代碼。我現在開始琢磨正確的模式。 – 2010-12-12 17:54:17

2

這是在this Apple document解釋。你不必這樣做。問題是,如果較低的泳池排水,不排水的上泳池也會自動排水。所以是的,釋放自動釋放的對象會被異常推遲,但它們最終會被釋放。

可以這樣說明:

NSAutoreleasePool* A = [[NSAutoreleasePool alloc] init]; 
NSAutoreleasePool* B = [[NSAutoreleasePool alloc] init]; 

    .... do many things. Put things in B ... 

NSAutoreleasePool* C = [[NSAutoreleasePool alloc] init]; 

    .... do many things. Put things in C ... 

[A drain]; // it automatically drains B and C, too! 

注意,您不必設置池經常。在每種方法中設置它肯定是一個矯枉過正的問題。通常情況下,您不會開始添加本地池,除非Instruments的實際測量結果表明在方法中存在如此多的自動釋放對象。

0

自動釋放池是特殊的,正常的,「如果你的新/分配/副本,你必須釋放」並不完全適用。當你創建一個自動釋放池時,它被放置在一個特殊的堆棧上。如果你忽略了一個池,那麼如果堆棧中的任何池被排空,它將被排空。在catch塊中排水不是必要的,但是例外情況可能會延遲池排水。有關詳細信息,請閱讀「Scope of Autorelease Pools and Implications of Nested Autorelease Pools」。

4

作爲一般事情:不,你不應該。在異常路徑上不應該耗盡池。相反,它的父級池會自動耗盡。 (這樣做會導致崩潰,如果進一步調用層次結構的代碼嘗試檢查異常,因爲您的finally子句將釋放它。)

例外情況是當您負責最外層的池,最有可能發生在多線程代碼中。在這種情況下,如果您希望代碼生成異常,則必須捕獲該異常並壓縮或處理異常,然後耗盡自動釋放池。 (不這樣做會導致崩潰,如果您自己沒有引發任何異常,實際上可以認爲它是可以接受的,因爲系統框架引發的任何異常都表示出現編程錯誤,並使程序處於未定義狀態。)

2

注意,在Mac OS X和iOS的應用程序異常一般只保留表示不可恢復的,程序員的錯誤。

如果通過框架拋出異常,的行爲是未定義的

例外是不被用於流控制。

(如別人說 - 不釋放在異常情況下,池父池將它清理乾淨。)

相關問題