看懂 iPhone view controller 記體體管理的原則(或者:didReceiveMemoryWarning 到底在搞什麼鬼?)
當 iPhone 或 iPod Touch 的記憶體不夠用的時候,當前 app 的所有 view controller 物件都會收到 didReceiveMemoryWarning
呼叫。你可以把它當成像 NSNotification 一樣的事件。
系統預設的 didReceiveMemoryWarning
實作(你一定用得到,因為你得呼叫 [super didReceiveMemoryWarning]
)是,如果當前 controller 所擁有的 view 沒有其他 superview (通常代表意思是沒被顯示出來,例如,埋藏在 navigation chain 或其他 tab 當中),那這個 view object 就會被是放掉。
如果你的 controller 只有這個 view 物件,事情就到此為止。不過我們都知道人生從來不是這麼簡單的。
尤其,如果你的 controller 當中宣告了從 nib 載入了 IBOutlet
物件。根據 Apple 文件,xib 載入的時候所有的 IBOutlet 物件都已經至少有了 retain count 1 。這些物件通常也都被你的 view 物件所擁有,所以通常等你用到他們的時候,他們的 retain count 都至少是 2 。
但是你的 view object 被放掉時,這些 IBOutlet 還被 retain 著。所以你要做兩件事:
- 在 view 被放掉時,也同樣放掉你的 IBOutlet
- 在你的 controller 被 dealloc 到時,也放掉你的 IBOutlet ,同時將他們的 pointer 設到 nil
iPhone SDK 一直到 2.2.1 都沒有好方法讓你做到 1. ,所以請參考 Craig Hockenberry 的這篇文章 ,簡言之就是要去 override -[UIViewController setView:]
才有辦法在 view 被放掉時也同時放掉那些 IBOutlet
。
(雖然事實上,如果你照著 Apple 最新的 coding 規範,將 IBOutlet 都設為 readwrite retain property,那麼在你的 controller 下次再度載入 view 時,這些舊 IBOutlet 都會被放掉,但是呢,如果你的 IBOutlet 很肥,例如滾輪式選單,那麼最好是放掉他們)
然後呢,在你的 controller 要被 dealloc
的時候,記得放掉你的 IBOutlet
物件,同時將指標設到 nil。Apple 文件有說明原因:因為當你最後呼叫 [super dealloc]
的時候,你的 view object 會被放掉,然後如果你已經做了 1. ,view 被放掉時去釋放 IBOutlet
,偏偏你早就已經在 dealloc
method 前半部 release 了他們,那麼接下來就會有人對死掉的指標值(所謂的「當夠了物件」──dangling object)送出 release
訊息,那就爆炸了…
總之,iPhone app 在記憶體使用上的最高指導原則是 lazy loading。要用的時候才載入,不用的時候就放掉且將指標值設為 nil ,最不容易出錯。這也是 Apple 從 Objective-C 2.0 時代開始大量推薦使用 @property 的另一個原因。
lukhnos :: Apr.30.2009 :: tekhnologia 技術或者藝術 :: 1 Comment »
One Response to “看懂 iPhone view controller 記體體管理的原則(或者:didReceiveMemoryWarning 到底在搞什麼鬼?)”
補充一點,2.0 式的 IBOutlet 物件宣告最好是長得像這樣,把 Outlet 宣告成 property:
@interface MyClass :NSObject {
Control *uiElement;
}
@property (nonatomic, retain) IBOutlet Control *uiElement;
@end
而不是
@interface MyClass :NSObject {
IBOutlet Control *uiElement;
}
@property (nonatomic, retain) Control *uiElement;
@end
這篇 StackOverflow 有比較詳細的討論:
http://stackoverflow.com/questions/61838/do-i-need-to-release-xib-resources