記憶體管理

由於 Java 預設沒有垃圾收集和 Objective-C,因此大部分 Java 開發人員第一個問題就是 J2ObjC 導入的記憶體管理方式。iOS 有兩種記憶體管理方法:參考計數和自動參考計數 (ARC)。

J2ObjC 會根據選擇的方法,產生不同的記憶體管理程式碼。翻譯 -use-arc 選項即可產生使用 ARC 的代碼。根據預設,這項設定會使用手動參照計數。

參考計數

參照計算方法會明確指出物件擁有權。方法在建立物件時會擁有該物件,直到釋放該物件為止。收到另一個方法的物件時,如果在方法傳回後需要參照該物件,接收方法會保留該物件。當方法不再需要參照物件時,必須將其釋放。如果物件的保留計數為零,系統就會釋放記憶體,物件也會失效。釋放物件時,系統會呼叫其 transactionloc() 方法,以釋出其執行個體變數的擁有權。

這種技巧的其中一個問題是轉移物件擁有權。例如,呼叫工廠方法可以建立物件。如果工廠方法在傳回物件前就釋出物件 (因為該物件不再想擁有該物件),則在呼叫方法可以保留該物件之前,系統會先釋放該物件。

如要轉移物件的擁有權,方法會向其傳送自動發布訊息 (而非發布訊息),該訊息將延遲發布訊息。如此一來,工廠方法就能建立要傳回的物件,並且在不撤銷物件的情況下,撤銷其擁有權。系統會按照固定間隔 (例如 iOS 應用程式中的每個事件迴圈疊代) 自動釋放集區,這表示該集區中的所有物件都會傳送延遲的發布訊息。任何保留次數降至零的物件都會照常釋放。

由於記憶體管理負擔是由開發人員負責,因此使用參照計數方法很容易流失記憶體。不過,Apple 會推薦一些最佳做法,以盡量減少這個問題,而 J2ObjC 也採用這種做法。

此外,執行階段和工具支援來偵測記憶體流失情形。Objective-C 執行階段會回報應用程式結束時偵測到的任何外洩情形,這是 J2ObjC 將 JUnit 測試轉譯為可執行二進位檔的原因之一。Xcode 使用 Clang,而編譯器對記憶體問題提供卓越的靜態分析,Xcode 則可透過其 Analyze 指令提供。

自動參考計數 (ARC)

ARC 是 Apple 建議使用的記憶體管理方法。該函式會將參照計數的責任移至編譯器,並在編譯期間新增適當的保留、發布和自動釋放方法。對於搭載 iOS 5 以上版本的裝置,ARC 支援低強度參照。

建議專案使用 ARC 執行翻譯的程式碼。轉譯後的 Objective-C 程式碼如同手寫的 Goal-C 程式碼。使用 ARC 有助於開發人員避免常見的記憶體相關錯誤 (例如過度釋放或參照不足),進而簡化記憶體管理作業,並降低發生錯誤的機率。

請注意,根據預設,ARC 並非例外狀況。具體來說,在擲回例外狀況時,記憶體會流失記憶體。由於例外狀況在 Java 中較為常見,且通常可以復原,因此這可能會引起問題。透過弧形進行編譯時,使用 -fobjc-arc-exceptions 將會修復外洩情形,但會產生一些可接受的效能成本。

我們也建議新專案將 ARC 用於手寫的 Objective-C 程式碼,而且只有在剖析資料顯示實際效能問題時,才會改回手動參照計數。ARC 和非 ARC 程式碼都可以編譯並連結至同一個應用程式,而不會發生問題。

低度參照

您可以使用 com.google.devtools.j2objc.Weak 註解欄位,轉譯器會使用此 API 產生遵循 Objective-C 弱參考語意的欄位。使用參照計數時,代表該欄位在初始化時不會保留,且會在包含的執行個體發布時自動釋放。使用 ARC 時,低強度欄位會以 __unsafe_unretained 註解標示,而相關屬性則宣告弱。

在某些情況下,內部類別執行個體會進入參考週期,並有外部執行個體。此處的 com.google.devtools.j2objc.WeakOuter 註解是用來標記內部類別,因此外部類別的參照會依照上述方式處理。內部類別中的其他欄位則不受此註解影響。

J2ObjC 也支援 WeakReference 類別,因此使用該類別的 Java 程式碼在翻譯後的運作方式也會相同。請注意,JVM 上具有 WeakReference 本身非確定性;希望在保持可預測性的情況下,希望避免記憶體流失的應用程式應改用 @Weak@WeakOuter

記憶體管理工具