萬盛學電腦網

 萬盛學電腦網 >> 網絡編程 >> ios >> iPhone開發內存管理

iPhone開發內存管理

   開發iPhone 應用程序並不難,基本上就是三個詞 - “memory, memory, memory” 。iPhone OS 對內存的要求很嚴格,有memory leak ,殺掉; 內存使用超限額,殺掉。一個經過測試的程序,在使用過程中90%以上的崩潰都是內存問題造成的。在這裡簡單總結一下Object-C 內存管理。

  基本概念

  Object-C 的內存管理基於引用計數(Reference Count)這種非常常用的技術。簡單講,如果要使用一個對象,並希望確保在使用期間對象不被釋放,需要通過函數調用來取得“所有權”,使用結束後再調用函數釋放“所有權”。“所有權”的獲得和釋放,對應引用計數的增加和減少,為正數時代表對象還有引用,為零時代表可以釋放。

  函數

  獲得所有權的函數包括

  alloc - 創建對象是調用alloc,為對象分配內存,對象引用計數加一。

  copy - 拷貝一個對象,返回新對象,引用計數加一。

  retain - 引用計數加一,獲得對象的所有權。

  另外,名字中帶有alloc, copy, retain 字串的函數也都認為會為引用計數加一。

  釋放所有權的函數包括

  release - 引用計數減一,釋放所有權。如果引用計數減到零,對象會被釋放。

  autorelease - 在未來某個時機釋放。下面具體解釋。

  autorelease

  在某些情況下,並不想取得所有權,又不希望對象被釋放。例如在一個函數中生成了一個新對象並返回,函數本身並不希望取得所有權,因為取得後再沒有機會釋放(除非創造出新的調用規則,而調用規則是一切混亂的開始),又不可能在函數內釋放,可以借助autorelease 。所謂autorelease , 可以理解為把所有權交給一個外在的系統(這個系統實際上叫autorelease pool),由它來管理該對象的釋放。通常認為交給 autorelease 的對象在當前event loop 中都是有效的。也可以自己創建NSAutoreleasePool 來控制autorelease的過程。

  據蘋果的人說,autorelease效率不高,所以能自己release的地方,盡量自己release,不要隨便交給autorelease來處理。

  規則

  引用計數系統有自己的引用規則,遵守規則就可以少出錯:

  獲得所有權的函數要和釋放所有權的函數一一對應。

  保證只有帶alloc, copy, retain 字串的函數才會讓調用者獲得所有權,也就是引用計數加一。

  在對象的 dealloc函數中釋放對象所擁有的實例變量。

  永遠不要直接調用dealloc來釋放對象,完全依賴引用計數來完成對象的釋放。

  有很多類都提供“便利構造函數(convenience constructors)”,它們創建對象但並不增加引用計數,意味著不需要調用release來釋放所有權。很好辨認,它們的名字中不會有alloc和copy。

  只要遵守這些規則,基本上可以消除所有Intrument可以發現的內存洩露問題。

  容器

  類似NSArray, NSDictionary, NSSet 等類,會在對象加入後引用計數加一獲得所有權,在對象被移除或者整個容器對象被釋放的時候釋放容器內對象的所有權。類似的情況還有UIView對 subview的所有權關系,UINavigationController對其棧上的controller的所有權關系等等。

  其他所有權的產生

  還有一些用法會讓系統擁有對象的所有權。比如NSObject 的performSelector:withObject:afterDelay 。如果有必要,需要顯示的調用cancelPreviousPerformRequestsWithTarget:selector:object: ,否則有可能產生內存洩露。

  因這種原因產生的洩露因為並不違反任何規則,是Intrument所無法發現的。

  循環引用

  所有的引用計數系統,都存在循環應用的問題。例如下面的引用關系:

  對象a創建並引用到了對象b.

  對象b創建並引用到了對象c.

  對象c創建並引用到了對象b.

  這時候b和c的引用計數分別是2和1。當a不再使用b,調用release釋放對b的所有權,因為c還引用了b,所以b的引用計數為1,b不會被釋放。b不釋放,c的引用計數就是1,c也不會被釋放。從此,b和c永遠留在內存中。

  這種情況,必須打斷循環引用,通過其他規則來維護引用關系。比如,我們常見的delegate往往是assign方式的屬性而不是retain方式的屬性,賦值不會增加引用計數,就是為了防止delegation兩端產生不必要的循環引用。如果一個UITableViewController 對象a通過retain獲取了UITableView對象b的所有權,這個UITableView對象b的delegate又是a, 如果這個delegate是retain方式的,那基本上就沒有機會釋放這兩個對象了。自己在設計使用delegate模式時,也要注意這點。

  因為循環引用而產生的內存洩露也是Instrument無法發現的,所以要特別小心。

  一些和內存管理相關的有用內容:

  Objective-C2.0 號稱可以支持Garbage Collection了, 也就是垃圾回收,但是我還沒在xcode以及文檔中找到相關的用法,也懶得去查了。 關於garbage collection的內容也沒啥可說的, 想說說這幾天遇到的無GC情況下的幾個內存相關問題。

  Objective-C 的autorelease確實給開發省了不少事情提高了開發效率, 這對於Mac OSX桌面開發沒問題,因為內存大不存在內存緊張的局面。但是如果要為iPhone開發程序, 還是慎用 autorelease的好, 否則只好等程序退出時再清理內存了。 在iphone上最好不要保存不必要的對象, 使用的時候在創建,比如圖片、文件等等。 還有一點要注意的就是UITableView, 如果你是把UITableView放在UITableViewCOntroller中,那麼別擔心,沒有什麼問題,如果你是在 UIViewController或者其子類裡放置UITableView,那麼注意了, 在Pop掉 viewcontroller的時候一定記得先把UItableView的delegate設置為空, 也就是 [tableView setDelegate:nil] ,之所以這樣做, 是因為tableView的delegate是個retain,會保存對象, 所以如果你不在pop之前將delegate設為nil, 將不會調用view controller的dealloc,內存也就無法釋放, 這麼來幾下恐怕就要內存吃緊了。

  總結了幾條內存使用經驗

  1. 對象現用現創建

  2. 所有用alloc,new , retain等創建的對象都需要調用release去釋放, 千萬別發送release消息給autorelease對象, 否則只能over了

  3. 注意delegate,如果時retain類型,最好在釋放之前將之設為nil

  4. 在頻繁使用alloc的地方(循環) 創建自己的NSAutoReleasePool

  5. 對於UIImage對象慎用 [UIImage imageNamed:], 使用[UIImage imageWithContentOfFile:] 或者[image initWithContentOfFile:]

copyright © 萬盛學電腦網 all rights reserved