本文會繼續深入學習OC記憶體管理,內容主要參考iOS高級編程,Objective C基礎教程,瘋狂iOS講義,是我學習記憶體管理的筆記 記憶體管理 1 記憶體管理的基本概念 1.1 Objective C中的記憶體管理 手動記憶體管理和自動釋放池 \ (Mannul Reference Counting) 自動 ...
本文會繼續深入學習OC記憶體管理,內容主要參考iOS高級編程,Objective-C基礎教程,瘋狂iOS講義,是我學習記憶體管理的筆記
記憶體管理
1 記憶體管理的基本概念
1.1 Objective-C中的記憶體管理
- 手動記憶體管理和自動釋放池---
MRC
>(Mannul Reference Counting) - 自動記憶體管理---
ARC
>(Automatic Reference Count) - 自動垃圾回收---
GC
>(Garbage Collection)
由於iOS系統不支持垃圾回收,所以我們在iOS開發中只能使用MRC和ARC來進行記憶體管理,本文不再介紹Objective-C中的垃圾回收機制,但是此處註意Objective-C中是存在垃圾回收機制的
1.2 記憶體管理中存在的問題
記憶體泄露
:不再需要的對象沒有釋放
引起的問題
:程式的記憶體占有量不斷增加,最終會被耗盡導致程式崩潰
野指針
:沒有進行初始化得指針
引起的問題
:浪費記憶體資源,如果調用程式會出現未知的結果,甚至導致程式崩潰
懸空指針
:一個指針指向一個被銷毀的對象
引起的問題
:調用懸空指針指向的屬性或者方法時,程式會出現未知的結果,甚至導致程式崩潰
僵屍對象
:過度釋放的對象
引起的問題
:
2.手動記憶體管理和自動釋放池---MRC
>(Mannul Reference Counting)
2.1 什麼是引用計數(Reference Counting)
引用計數
:Objective-C中引入了引用計數這一機制來跟蹤並處理對象的生命周期,
管理方式
:每個對象都有一個與之關聯的整數,這個整數被稱為引用計數,在Objective-C中,通過不同的方法可以對引用計數進行操作,具體的處理如下表:
對象操作 | Objective-C方法 | 對應的操作結果 |
---|---|---|
生成並持有對象 | alloc , new , copy ,mutableCopy 等方法 |
生成對象並設置引用計數 =1 |
持有對象 | reatain 方法 |
使引用計數 +1 |
釋放對象 | release 方法 |
使引用計數 -1 |
廢棄對象 | dealloc 方法---系統自動調用 |
引用計數 =0 時調用 |
關於delloc
方法:dealloc方法繼承自NSObject
,因此所有的對象都具有此方法,當一個對象的引用計數為0時,也就意味著沒有任何程式需要此對象,系統會回收該對象所占用的記憶體,在系統銷毀對象之前,會自動調用該對象的dealloc方法來執行一些回收操作,如果該對象還持有其他對象的引用,我們必須重寫dealloc方法來釋放該對象引用的其他對象(通常就是使用該對象的release方法)
引用計數機制回收對象的說明
:如果一個對象的引用計數為0,則表明程式已經不再需要它,這時系統會自動回收該對象所占記憶體,相反,如果一個對象的引用計數不為0,系統就不應該回收,也不會回收它所占的記憶體
關於retainCount方法
:Objective-C提供了retainCount方法來返回一個對象當前的引用計數
如何重寫dealloc方法
:
- (void)dealloc {
// 處理該對象的其他引用(通過release方法)
/** 回調父類的dealloc方法 */
[super dealloc];
}
2.2 蘋果如何管理引用計數
- 2.2.1 因為NSObject類的源代碼沒有公開,我們利用Xcode的調試器(lldb)和iOS大概追溯出其實現過程
alloc
+alloc +allocWithZone: class_createInstance //此方法可以通過objc4中的runtime/objc-runtime-new.mm確認 calloc // 分配記憶體塊
retainCount
-retainCount __CFDoExternRefOperation // 此函數根據retain,retainCount,release操作進行分發,調用__CFBasicHashXXX方法 CFBasicHashGetCountOfKey
retain
-retain __CFDoExternRefOperation CFBasicHashAddValue
release
-release __CFDoExternRefOperation CFBasicHashRemoveValue // 當此函數返回0時, -release調用dealloc方法
- 2.2.2 由__CFDoExternRefOperation函數以及此函數的調用關係,我們大概推算蘋果大概是使用散列表(引用計數表)來管理引用計數
- 通過引用計數表來管理引用計數的好處:
- 對象用記憶體塊的分配無須考慮記憶體塊頭部
- 引用計數表各記錄中存有記憶體塊的地址,可從各個記錄追溯到各對象的記憶體塊(在進行記憶體泄露的檢查時,此條特性具有舉足輕重的作用,即使出現故障導致對象占用的記憶體塊損壞,但是只要引用計數表沒有被破壞,我們就可以確定各記憶體塊的位置,這就是設置全局斷點可以查出哪裡出現記憶體泄露的原因)
- 通過引用計數表來管理引用計數的好處:
2.3 記憶體管理的思考方式
自己生成的對象,自己持有
1.1 使用
alloc
new
copy
mutableCopy
創建的對象只能自己持有id obj1 = [[NSObject alloc] init]; id obj2 = [NSObject new]; id obj3 = [NSObject copy]; id obj4 = [NSObject mutableCopy];
1.2 使用以上名稱的開頭的方法也意味著自己生成並持有對象
alloc
NewObjectnew
NewObjectcopy
NewObjectmutableCopy
NewObject非自己生成的對象,自己也能持有
2.1 非
alloc
new
copy
mutableCopy
生成的對象,變數obj本身不持有該對象id obj1 = [NSMutableArray array]; id obj2 = [NSDictionary dictionary];
2.2 通過retain方法,非通過
alloc
new
copy
mutableCopy
生成的對象,可以成為自己持有的對象id obj = [NSMutableArray array]; [obj retain];
不再需要自己持有的對象時釋放
- 3.1 釋放通過
alloc
new
copy
mutableCopy
生成的對象,一旦不在需要,務必要使用release方法釋放
```
id obj = [[NSObject alloc] init];[obj release];
```- 3.2 用retain方法持有的非自己生成的對象,一旦不再需要,也一定要使用release釋放
id obj = [NSMutableArray array]; [obj retain]; // 通過retain方法持有對象 [obj release]; // 在不需要時也要通過release方法釋放對象
3.3 用某個方法生成對象,並將其作為方法的返回值,這時我們該如何處理
3.3.1 通過
```alloc
new
copy
mutableCopy
或其他符合命名規則的方法生成的對象,只需要原封不動的返回就能讓調用方也持有該對象(id)allocObject {
id obj = [[NSObject alloc] init];
return obj;
}(id)allocObjectWithObject:(id)obj {
id object = [obj allocObject];
return object;
}
```
3.3.2 如果持有非自己生成的對象,例如[NSMutableArray array]生成的對象,我們要使用autorelease方法釋放
註:
命名規則
:用來取得誰都不持有的對象的方法名不能以alloc
new
copy
mutableCopy
開頭- (id)object { id obj = [NSMutableArray array]; [obj autorelease]; return obj; }
3.3.3
autorelease
方法:提供了這樣的功能,使對象在超出指定的生存範圍時自動並正確釋放(調用release方法)
- 3.1 釋放通過
非自己持有的對象無法釋放---註意以下兩點,如果發生這樣的情況會導致程式崩潰
4.1 通過
alloc
new
copy
mutableCopy
方法或者通過retain方法持有的對象,一旦不再需要時,必須進行釋放,除此之外其他方法獲得的對象絕對不能釋放,一旦釋放會造成程式崩潰4.2 自己持有的對象釋放後再次釋放,造成僵死對象,引起程式崩潰或在訪問廢棄的對象時崩潰
id obj = [[NSObject alloc] init]; [obj release]; [obj release]; // 再次釋放