標籤:bubuko 情境 整數 nsobject 5.0 ble 類型 property 重寫
一、 基本原理
1. 什麼是記憶體管理
- 行動裝置的記憶體極其有限,每個app所能佔用的記憶體是有限制的
- 當app所佔用的記憶體較多時,系統會發出記憶體警告,這時得回收一些不需要再使用的記憶體空間。比如回收一些不需要使用的對象、變數等
- 管理範圍:任何繼承了NSObject的對象,對其他基礎資料型別 (Elementary Data Type)(int、char、float、double、struct、enum等)無效
2. 對象的基本結構
- 每個OC對象都有自己的引用計數器,是一個整數,表示“對象被引用的次數”,即有多少人正在使用這個OC對象
- 每個OC對象內部專門有4個位元組的儲存空間來儲存引用計數器
3. 引用計數器的作用
- 當使用alloc、new或者copy建立一個新對象時,新對象的引用計數器預設就是1
- 當一個對象的引用計數器值為0時,對象佔用的記憶體就會被系統回收。換句話說,如果對象的計數器不為0,那麼在整個程式運行過程,它佔用的記憶體就不可能被回收,除非整個程式已經退出
4. 引用計數器的操作
- 給對象發送一條retain訊息,可以使引用計數器值+1(retain方法返回對象本身)
- 給對象發送一條release訊息,可以使引用計數器值-1
- 可以給對象發送retainCount訊息獲得當前的引用計數器值
5. 對象的銷毀
- 當一個對象的引用計數器值為0時,那麼它將被銷毀,其佔用的記憶體被系統回收
- 當一個對象被銷毀時,系統會自動向對象發送一條dealloc訊息
- 一般會重寫dealloc方法,在這裡釋放相關資源,dealloc就像對象的遺言
- 一旦重寫了dealloc方法,就必須調用[super dealloc],並且放在最後面調用
- 不要直接調用dealloc方法
- 一旦對象被回收了,它佔用的記憶體就不再可用,堅持使用會導致程式崩潰(野指標錯誤)
二、 Xcode的設定1. 取消ARC
要想手動調用retain、release等方法,在建立項目的時候不要勾選ARC
2. 開啟殭屍對象監控
預設情況下,Xcode是不會管殭屍對象的,使用一塊被釋放的記憶體也不會報錯。為了方便調試,應該開啟殭屍對象監控
三、 記憶體管理原則1. 原則分析
- 只要還有人在用某個對象,那麼這個對象就不會被回收
- 只要你想用這個對象,就讓對象的計數器+1
- 當你不再使用這個對象時,就讓對象的計數器-1
2. 誰建立,誰release
- 如果你通過alloc、new或[mutable]copy來建立一個對象,那麼你必須調用release或autorelease
- 換句話說,不是你建立的,就不用你去[auto]release
3. 誰retain,誰release
- 只要你調用了retain,無論這個對象是如何產生的,你都要調用release
4. 總結
- 有始有終,有加就有減
- 曾經讓對象的計數器+1,就必須在最後讓對象計數器-1
四、 set方法的記憶體管理
如果你有個OC物件類型的成員變數,就必須管理這個成員變數的記憶體。比如有個Book *_book
1. set方法的實現
- (void)setBook:(Book *)book{ if (book != _book) { [_book release]; _book = [book retain]; }}
2. dealloc方法的實現
1 - (void)dealloc {2 3 [_book release];4 5 [super dealloc];6 7 }
五 @property參數1. 控制set方法的記憶體管理
- retain : release舊值,retain新值(用於OC對象)
- assign : 直接賦值,不做任何記憶體管理(預設,用於非OC物件類型)
- copy : release舊值,copy新值(一般用於NSString *)
2. 控制需不需產生set方法
- readwrite :同時產生set方法和get方法(預設)
- readonly :只會產生get方法
3. 多線程管理
- atomic:效能低(預設)
- nonatomic :效能高
4. 控制set方法和get方法的名稱
- setter : 設定set方法的名稱,一定有個冒號:
- getter : 設定get方法的名稱
六、循環參考1. @class
對於循環相依性關係來說,比方A類引用B類,同時B類也引用A類
這種代碼編譯會報錯。當使用@class在兩個類相互聲明,就不會出現編譯報錯
使用 @class 類名; 就可以引用一個類,說明一下它是一個類
l #import方式會包含被引用類的所有資訊,包括被引用類的變數和方法;@class方式只是告訴編譯器在A.h檔案中 B *b 只是類的聲明,具體這個類裡有什麼資訊,這裡不需要知道,等實現檔案中真正要用到時,才會真正去查看B類中資訊
l 如果有上百個標頭檔都#import了同一個檔案,或者這些檔案依次被#improt,那麼一旦最開始的標頭檔稍有改動,後面引用到這個檔案的所有類都需要重新編譯一遍,這樣的效率也是可想而知的,而相對來 講,使用@class方式就不會出現這種問題了
l 在.m實現檔案中,如果需要引用到被引用類的實體變數或者方法時,還需要使用#import方式引入被引用類
2. 迴圈retain
- 比如A對象retain了B對象,B對象retain了A對象
- 這樣會導致A對象和B對象永遠無法釋放
3. 解決方案
- 當兩端互相引用時,應該一端用retain、一端用assign
七、 autorelease1. autorelease
- 給某個對象發送一條autorelease訊息時,就會將這個對象加到一個自動釋放池中
- 當自動釋放池銷毀時,會給池子裡面的所有對象發送一條release訊息
- 調用autorelease方法時並不會改變對象的計數器,並且會返回對象本身
- autorelease實際上只是把對release的調用延遲了,對於每一次autorelease,系統只是把該對象放入了當前的autorelease pool中,當該pool被釋放時,該pool中的所有對象會被調用Release
2. 自動釋放池的建立
@autoreleasepool
{
// ....
}
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// .....
[pool release]; // 或[pool drain];
- 在程式運行過程中,可以建立多個自動釋放池,它們是以棧的形式存在記憶體中
- OC對象只需要發送一條autorelease訊息,就會把這個對象添加到最近的自動釋放池中(棧頂的釋放池)
3. 應用執行個體
l 以前:
Book *book = [[Book alloc] init];[book release];
l 現在:
Book *book = [[[Book alloc] init] autorelease];
// 不要再調用[book release];
+ (id)book { return [[[Book alloc] init] autorelease];}
外界調用[Book book]時,根本不用考慮在什麼時候釋放返回的Book對象
4. 規律
- 一般來說,除了alloc、new或copy之外的方法建立的對象都被聲明了autorelease
- 比如下面的對象都已經是autorelease的,不需要再release
NSNumber *n = [NSNumber numberWithInt:100];NSString *s = [NSString stringWithFormat:@"jack"];NSString *s2 = @"rose";
IOS記憶體管理詳解