第9章 內存管理
9.1 對象生命週期
9.1.1 引用計數
一個對象被從堆中分配出來之後,我們需要明確的知道是誰擁有了這個對象,因爲只有擁有這個對象的所有者能夠銷燬它。但我們在實際使用過程中, 這個對象可能被傳遞給另一個對象(例如通過傳遞指針參數),一旦這個過程複雜,我們很難確定誰最後擁有了這個對象。
使用引用計數就可以拋開這個問題,我們不需要再去關心誰擁有了這個對象,因爲我們把管理權交割給了對象自己。當這個對象不再被任何人使用時,它自己負責銷燬自己。
引用計數器(保留計數器):每個對象都有一個與之相關的整數,當某段代碼需要訪問一個對象時,該代碼就將該對象的保留計數器值加1,表示“我要訪問該對象”;反之,則減1,表示不再訪問該對象。當保留計數器值爲0時,表示不再有代碼訪問該對象,它將被銷燬。
使用alloc、new方法或通過copy消息創建一個對象時,對象保留計數器值置爲1。
-(id) retain; 計數加一,返回id類型的值,可以在接受消息同時調用retain
-(oneway void) release; 計數減一
-(NSUInteger) retainCount; 獲取計數值
保留計數器值爲0將被銷燬時,Objective-C會自動發送一條dealloc消息,可以重寫dealloc方法來釋放特殊的資源,dealloc方法會在對象銷燬時自動調用。
9.1.3 訪問方法中的保留和釋放
一種合理的訪問方法
- (void) setEngine: (Engine *) newEngine
{
[newEngine retain];
[engine release];
engine = newEngine;
}
9.1.4 自動釋放
自動釋放池(autorelease pool),一個存放對象的池(集合),能夠自動釋放。
NSObject類提供了一個autorelease方法:
- (id) autorelease;
該方法預設定了一條在將來某個時間發送的release消息,其返回值是接收消息的對象。當給一個對象發送autorelease消息時,實際上是將該對象添加到NSAutoreleasePool中。當自動釋放池被銷燬時,會向該池中所有對象發送release消息。
示例
- (NSString *) description
{
NSString * description;
description = [[NSString alloc] initWithFormat: @"I am %d years old", 4];
return([description autorelease]);
}
用下面的代碼調用
NSLog(@"%@", [someObject descripion]);
自動釋放池的創建和銷燬
自動釋放池以棧的形式實現,創建和銷燬的代價很小。
通過@autoreleasepool關鍵字(推薦使用)
使用@autorelease{}時,任何在花括號裏定義的變量在花括號外無法使用
通過NSAutoreleasePool對象
NSAutoreleasePool *pool=[NSAutoreleasePool new];
…
[pool release];//釋放該池
在使用AppKit或UIKit的時候,自動釋放池會在明確的時間創建或釋放,比如在處理當前用戶事件的時候。
9.2 Cocoa的內存管理規則
- 當使用new、alloc或copy方法創建一個對象時,對象的保留計數器數值爲1。當不再使用該對象時,應該向該多想發送release或autorelease消息,銷燬對象
- 當通過其他方法獲得一個對象時,假設該對象的保留計數器的值爲1,而且已經被設置爲自動釋放,則不需要執行任何操作來確保該對象得到清理。如果打算在一段時間內擁有該對象,則需要保留它並確保在操作完成時釋放它
- 如果保留了某個對象,就需要(最終)釋放或自動釋放該對象。必須保持retain和release方法的使用次數相等
如果我使用了new,clloc或copy方法獲得了一個隊,則我必須釋放或自動釋放該對象。
內存管理規則
獲得途徑 | 臨時對象 | 擁有對象 |
---|---|---|
alloc/new/copy | 不再使用時釋放對象 | 在dealloc方法中釋放對象 |
任何其他方法 | 不需要執行任何操作 | 獲得對象時保留,在dealloc方法中釋放對象 |
自動釋放池被清理的時間是完全確定的:要麼是在代碼中你自己動手銷燬,要麼是使用AppKit時在事件循環結束時銷燬。
9.2.3 垃圾回收
Objective-C 2.0引入的自動內存管理機制,也稱垃圾回收。
iOS中無法使用垃圾回收的,主要原因是無法知道垃圾回收器什麼時候會起作用。
9.2.4 自動引用計數
ARC在編譯時進行工作,會在需要的地方自動插入retain和release語句,無需自己動手。
1、 有時用weak會好一些
當指針志向某個對象時,如果管理它的內存(通過retain和release),就擁有這個對象的強引用(strong reference),不管理,擁有弱引用。
保留循環(retain cycle):循環引用導致內存泄漏
弱引用和歸零弱引用可以解決這個問題
2、 一輛新車
Xcode可以將已有的項目轉換爲支持ARC的。但必須禁用垃圾回收機制。
3、 擁有者權限
爲了便於ARC工作,必須告訴編譯器哪個對象時指針的擁有者
9.3 異常
支持異常特性,需要在Xcode中啓用Enable Objective—C Exceptions項。
捕獲異常
@try{
} @catch(NSException *exception){
} @finally{
}
拋出異常
@throw theException或 [theException raise]
@try{
NSException *e=...;
@throw e;
} @catch(NSException *exception){
@throw; //rethrow e.
} @finally{
}
這裏finally代碼塊會在@throw引發下一次異常處理調用前執行,@finally是在@throw發生之前調用。