MRC期間的記憶體管理方式
MRC引用計數規則
記憶體管理的範圍:任何繼承了NSObject的物件,對基本資料型別無效(系統會自動回收)
相關名詞:
- 記憶體洩漏:程式未能釋放已經不在使用的記憶體
- 殭屍物件:物件被銷燬,不能再使用
- 野指標(iOS):指標指向的物件已經被銷燬,指向殭屍物件的指標,準確的說iOS中的野指標應該叫懸垂指標,對於野指標,要及時將其賦值為nil,成為空指標
- 空指標:沒有指向任何物件的指標
記憶體管理的思考方式:
- 自己生成的物件,自己持有
- 非自己生成的物件,也能持有
- 不再需要自己持有的物件時,釋放
- 非自己持有的物件無法釋放
操作 | 方法 | 思考方式 |
---|---|---|
生成物件 | alloc/new/copy/mutableCopy | 自己生成,自己持有 |
持有物件 | retain | 非自己生成,也能持有 |
釋放物件 | release | 不再需要時,釋放 |
廢棄物件 | dealloc |
相關方法
- alloc/new/copy/mutableCopy:生成物件,引用計數初值為1
- retain:引用計數+1,並返回+1後的當前例項物件
- release:引用計數-1,並沒有釋放記憶體
- dealloc:例項物件銷燬前,自動呼叫;引用計數為0,自動呼叫
release與dealloc的區別:release釋放的是對該物件的所有權,dealloc釋放的是該物件佔用的記憶體
自己生成,自己持有(alloc/new/copy/mutableCopy)
//生成並持有物件
id obj1 = [[NSObject alloc] init];
id obj2 = [NSObject new];
NSString *str = @"123";
NSString *str2 = [str copy]; //生成並持有str的副本
NSMutableArray *array1 = [NSMutableArray alloc]init];
NSMutableArray *array2 = [array1 mutableCopy]; //生成並持有array1的副本
copy方法利用基於NSCopying方法約定,由各類實現的copyWithZone:方法生成並持有物件的副本。mutableCopy
方法利用基於NSMutableCopying:方法約定,由各類實現的mutableCopyWithZone:
生成並持有物件的副本。
區別:copy生成不可變更的物件,mutableCopy生成可變更的物件
注:以alloc/new/copy/mutableCopy開頭,使用駝峰命名法的方法,也是自己生成並持有物件
非自己生成,也能持有
通過retain方法,讓不是自己生成的物件與使用alloc/new/copy/mutableCopy方法生成的物件一樣都能被持有。
//取得,但不持有物件
NSArray *array = [NSArray array];
//持有物件
[array retain]
不再需要的物件,釋放
alloc/new/copy/mutableCopy生成並持有的物件,或者retain方法持有的物件,一旦不需要,無比要使用release進行釋放。
{
//取得,但不持有物件
id obj = [NSMutableArray array];
//持有物件
[obj retain];
//釋放物件
[obj release];
}
注:
記憶體中的常量物件(類物件、常量字串物件等)的記憶體分配空間與其他物件不同,沒有引用計數機制,永遠不能釋放這些物件,獲取的retainCount結果返回為NSUIntegerMax(最大的無符號整數)
非自己持有的物件無法釋放
釋放後,再次釋放
id obj = [NSObject alloc]init];
[obj release];
[obj release];
取得物件存在,但不持有時釋放
id obj = [NSObject object];
[obj release];
這些都會導致程式的崩潰
MRC下autorelease的使用
autorelease的具體使用方法:
- 生成並持有NSAutoreleasePool物件
- 呼叫已分配物件的autorelease例項方法
- 廢棄NSAutoreleasePool物件
while(...) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
/*
在此進行一系列操作,呼叫臨時物件的autorelease方法
*/
[pool drain]; // 廢棄pool
}
注:
- 需要長時間執行的程式碼段或大量使用臨時物件的程式碼可以通過autoreleasepoool提高記憶體利用率
- 可以給例項多次傳送retain,相應的也可以給例項多次傳送autorelease,只要autorelease和retain要成對傳送
release與autorelease的區別
release:呼叫後,立即釋放物件
autorelease:物件在超出指定的生存範圍是能夠自動並正確的釋放(呼叫release方法)
過程:[obj autorelease]
不立即釋放變數obj所指的物件,而是註冊到autoreleasepool中,pool結束時自動呼叫release
- 以alloc為頭,使用駝峰命名法的方法,如何實現自己生成,自己持有的?
- (id)allocObject
{
//obj生成並持有物件
id obj = [NSObject alloc]init];
return obj;
}
id obj = [NSObject allocObject];
- 如何實現[NSArray array]這種取得物件存在,但並不持有的?
- (id)object
{
//obj生成並持有物件
id obj = [[NSObject alloc]init];
//obj註冊到autoreleasepool中,這裡的autoreleasepool一般都是main函式最開始生成的自動釋放池
[obj autorelease];
return obj;
}
//取得物件,並不持有
id obj = [NSObject object];
[obj retain]; //持有物件
MRC中要重寫的方法
- dealloc:徹底釋放一個物件,還需要釋放該物件所持有的所有的物件的所有權
- (void)dealloc
{
//所有持有的物件呼叫release方法,釋放所有權
[super dealloc]; //一定要呼叫父類的dealloc,記憶體的釋放才會從子類一直上升到NSObject,物件才會徹底被釋放
}
- setter方法
基本型別:
- (void)setObj:(NSUInteger)obj
{
_obj = obj;
}
物件型別:
//以下寫法引數obj與_obj是同一物件時,會出問題
- (void)setObj:(id)obj {
//obj與_obj都只指向同一物件,但obj不持有物件,這時release後物件
//引用計數為0,物件被銷燬,然後呼叫retain會出問題
[_obj release];
_obj = [obj retain];
}
//兩種更安全的寫法
- (void)setObj:(id)obj
{
//新物件所有權+1
[obj retain];
//舊物件所有權釋放
[_obj release];
//將新物件賦給_obj
_obj = obj;
}
- (void)setObj:(id)obj
{
if(_obj != obj)
{
//釋放舊物件所有權
[_obj release];
//賦給_obj所有權+1後的新物件
_obj = [obj retain];
}
}
重寫過後的setter方法與例項變數直接賦值的比較:
id obj = [[NSObject alloc]init];
self.obj = obj; //重寫過setter方法後,所有權會+1
_obj = obj; //例項變數直接賦值,所有權不變
3、getter方法
getter方法不屬於alloc/new/copy/mutableCopy的自己生成自己持有,與[NSArray array]
一樣,都是取得非自己生成物件但不持有
- (id)obj
{
id tmp = [obj retain];
return [tmp autorelease]; //註冊到autoreleasepool中,實現取得非自己生成物件但不持有,這裡的autoreleasepool通常都是main函式裡最開始生成的那個
}
int main(void) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Object *obj = [Object obj];
[pool drain];
}
總結:
- 自己生成物件自己持有,只適用於以alloc/new/copy/mutableCopy開頭的方法。
- MRC的引用計數規則下,持有方法與釋放方法一定要成對使用,確保物件被正確釋放
- 其他方式獲得的物件,都屬於取得非自己生成的物件,所以需要呼叫autorelease方法放入到autoreleasepool中。
參考資料:
- Objective-C程式設計全解
- Objective-C高階程式設計iOS與OS X多執行緒和記憶體管理
相關文章
- iOS 記憶體管理MRCiOS記憶體
- MRC 時代的記憶體管理記憶體
- Redis 過期時間與記憶體管理Redis記憶體
- 作業系統——記憶體連續分配管理方式作業系統記憶體
- JVM記憶體分為3個記憶體空間JVM記憶體
- Redis的記憶體回收機制和記憶體過期淘汰策略詳解Redis記憶體
- JavaScript之記憶體空間JavaScript記憶體
- CPU、記憶體、磁碟IO之間的關係記憶體
- JVM元空間Metaspace的記憶體結構JVM記憶體
- Java的記憶體 -JVM 記憶體管理Java記憶體JVM
- [Redis]過期刪除和記憶體淘汰Redis記憶體
- 程序間通訊(3)-共享記憶體記憶體
- java記憶體間互動規則Java記憶體
- 程式間通訊之共享記憶體記憶體
- Kubernetes中Pod間共享記憶體方案記憶體
- 10-記憶體空間佈局記憶體
- 記憶體管理篇——實體記憶體的管理記憶體
- 【進階1-3期】JavaScript深入之記憶體空間詳細圖解JavaScript記憶體圖解
- linux記憶體管理(一)實體記憶體的組織和記憶體分配Linux記憶體
- JS中的棧記憶體、堆記憶體JS記憶體
- Redis記憶體——記憶體消耗(記憶體都去哪了?)Redis記憶體
- Linux程式間通訊之共享記憶體Linux記憶體
- Linux使用者空間記憶體管理Linux記憶體
- 什麼是Java記憶體模型(JMM)中的主記憶體和本地記憶體?Java記憶體模型
- 【記憶體管理】記憶體佈局記憶體
- 記憶體管理 記憶體管理概述記憶體
- ArkTS 的記憶體空間詳解:從 SemiSpace 到 HugeObjectSpace記憶體Object
- 記憶體的分配與釋放,記憶體洩漏記憶體
- 深入理解javascript系列(二):記憶體空間JavaScript記憶體
- Kernel SIG直播:讓人頭疼的“核心記憶體被改”和“記憶體洩露”怎麼解?|第13期記憶體洩露
- 聊聊 記憶體模型與記憶體序記憶體模型
- Go:記憶體管理與記憶體清理Go記憶體
- NIO的JVM記憶體和機器記憶體的選擇JVM記憶體
- Redis 中的過期刪除策略和記憶體淘汰機制Redis記憶體
- ArkTS 的記憶體快照與記憶體洩露除錯記憶體洩露除錯
- Java虛擬機器的記憶體空間有幾種Java虛擬機記憶體
- Java虛擬機器的記憶體空間有幾種!Java虛擬機記憶體
- JavaScript的記憶體空間、賦值和深淺拷貝JavaScript記憶體賦值