108. 內(nèi)存管理概述
1. 內(nèi)存管理
內(nèi)存的作用:儲存數(shù)據(jù)。
1). 如何將數(shù)據(jù)存儲到內(nèi)存之中。
聲明1個(gè)變量,然后將數(shù)據(jù)存儲進(jìn)去。
2). 當(dāng)數(shù)據(jù)不再被使用的時(shí)候, 占用的內(nèi)存空間如何被釋放。
2. 內(nèi)存中的5大區(qū)域。
棧:局部變量。當(dāng)局部變量的作用域,被執(zhí)行完畢之后,局部變量就會被系統(tǒng)立即回收。
堆:OC對象、使用C函數(shù)申請的動(dòng)態(tài)空間。
BSS段:未初始化的全局變量、靜態(tài)變量。一旦初始化就回收,并轉(zhuǎn)存到數(shù)據(jù)段之中。
數(shù)據(jù)段:已經(jīng)初始化的全局變量、靜態(tài)變量。直到程序結(jié)束的時(shí)候才會被回收。
代碼段:代碼,程序結(jié)束的時(shí)候,系統(tǒng)會自動(dòng)回收存儲在代碼段中的數(shù)據(jù)。
棧、BSS端、數(shù)據(jù)段、代碼段 存儲在它們中的數(shù)據(jù),數(shù)據(jù)的回收是由系統(tǒng)自動(dòng)完成的,不需要程序員干預(yù)。
3. 分配在堆區(qū)的OC對象,是肯定需要被回收的?。。。。?!
存儲在堆中的OC對象,系統(tǒng)不會自動(dòng)回收。直到程序結(jié)束的時(shí)候,才會被回收。
iPhone機(jī)制:40MB 警告、45Mb 警告、120Mb 閃退。
4. 內(nèi)存管理的范圍:
只需要管理存儲在堆中的OC對象的回收。其他區(qū)域中的數(shù)據(jù)的回收,是系統(tǒng)自動(dòng)管理的。
109. 引用計(jì)數(shù)器
5. 對象什么時(shí)候被回收?
當(dāng)有人使用這個(gè)對象的時(shí)候,這個(gè)對象就不能被回收。
只有在沒有任何人,使用這個(gè)對象的時(shí)候,才可以回收。
6. 引用計(jì)數(shù)器
1). 每個(gè)對象,都有1個(gè)屬性,叫做retainCount。(引用計(jì)數(shù)器,默認(rèn)值是1)。類型是unsigned long 占據(jù)8個(gè)字節(jié)。
引用計(jì)數(shù)器的作用:用來記錄當(dāng)前這個(gè)對象,有多少人在使用它。
默認(rèn)情況下,創(chuàng)建1個(gè)對象出來,這個(gè)對象的引用計(jì)數(shù)器的默認(rèn)值是1.
2). 當(dāng)多1個(gè)人使用這個(gè)對象的時(shí)候。應(yīng)該先讓這個(gè)對象的引用計(jì)數(shù)器+1,代表這個(gè)對象多1個(gè)人使用。
3). 當(dāng)這個(gè)對象少1個(gè)人使用的時(shí)候,應(yīng)該想讓這個(gè)對象的引用計(jì)數(shù)器-1,代表這個(gè)對象少1個(gè)人使用。
4). 當(dāng)這個(gè)對象的引用計(jì)數(shù)器變?yōu)?的時(shí)候。代表這個(gè)對象無人使用。這個(gè)時(shí)候系統(tǒng)就會自動(dòng)回收這個(gè)對象。
7. 如何操作引用計(jì)數(shù)器?
1). 為對象發(fā)送1條retain消息。對象的引用計(jì)數(shù)器就會+1。當(dāng)多1個(gè)人使用對象的時(shí)候才發(fā)。
2). 為對象發(fā)送1條release消息,獨(dú)享的引用計(jì)數(shù)器就會-1。當(dāng)少1個(gè)人使用對象的時(shí)候才發(fā)。
3). 為對象發(fā)送1條retainCount消息。就可以取到對象的引用計(jì)數(shù)器的值。
就這樣+ ,- 當(dāng)對象的引用計(jì)數(shù)器變?yōu)?的時(shí)候,對象就會被系統(tǒng)立即回收。
在對象被回收的時(shí)候,會自動(dòng)調(diào)用對象的dealloc方法。
110. 內(nèi)存管理分類。
MRC:Manual Reference Counting 手動(dòng)引用計(jì)數(shù)(手動(dòng)內(nèi)存管理)
當(dāng)多1個(gè)人使用對象的時(shí)候,要求程序員手動(dòng)的發(fā)送retain消息;
當(dāng)少1個(gè)人使用的時(shí)候,程序員手動(dòng)發(fā)動(dòng)release消息。
ARC:Automatic Reference Counting 自動(dòng)引用技術(shù)(自動(dòng)內(nèi)存管理)
系統(tǒng)自動(dòng)的在合適的地方,發(fā)送retain或release消息。
學(xué)習(xí)MRC的理由:
1). 面試必考;
2). 早起的APP開發(fā),使用的是MRC技術(shù)。
3). iOS大牛,都是從MRC成長起來的。
4). ARC是基于MRC的。
111. 第一個(gè)MRC程序。
關(guān)閉ARC:選擇項(xiàng)目--BuildSettings---Objective-C Automatic Reference Counting. NO。
XCode7 默認(rèn)支持ARC開發(fā),默認(rèn)使用的開發(fā)方式就是ARC的模式。
2. 當(dāng)系統(tǒng)的引用計(jì)數(shù)器變?yōu)?的時(shí)候,系統(tǒng)會自動(dòng)回收對象。
在系統(tǒng)回收對象的時(shí)候,會自動(dòng)的調(diào)用對象的dealloc方法。
只要這個(gè)方法被執(zhí)行,就代表當(dāng)前這個(gè)對象被回收了。
重寫dealloc方法的規(guī)范:
必須要調(diào)用父類的dealloc方法。并且要放在最后一句代碼。
@implementation Person
- (void)dealloc {
NSLog(@"名字為%@的人,掛了??!", _name);
[super dealloc];
}
@end
Person *p1 = [[Person alloc] init];
p1.name = @"小明";
NSUInteger count = [p1 retainCount];
NSLog(@"count = %lu", count);
[p1 release];
3. 測試引用計(jì)數(shù)器:
1). 新創(chuàng)建1個(gè)對象,這個(gè)對象的引用計(jì)數(shù)器的值默認(rèn)是1;
2). 當(dāng)對象的引用計(jì)數(shù)器變?yōu)?的時(shí)候。對象就會被立即回收,并自動(dòng)調(diào)用dealloc方法。
3). 為對象發(fā)送retain消息,對象的引用計(jì)數(shù)器+1。
為對象發(fā)送release消息,并不是回收對象。而是讓對象的引用計(jì)數(shù)器-1;
當(dāng)對象的引用計(jì)數(shù)器值變?yōu)?的時(shí)候。對象才會被系統(tǒng)立即回收。
112. 內(nèi)存管理的原則
1.內(nèi)存管理的重點(diǎn)
1). 什么時(shí)候給對象發(fā)送retain消息;
當(dāng)多1個(gè)人使用這個(gè)對象的時(shí)候,應(yīng)該先為這個(gè)對象發(fā)送retain消息。
2). 什么時(shí)候給對象發(fā)送release消息;
當(dāng)少1個(gè)人使用這個(gè)獨(dú)享的時(shí)候,應(yīng)該為這個(gè)對象發(fā)送1條release消息。
- 在ARC機(jī)制下,retain release dealloc這些方法是自動(dòng)調(diào)用的。這些方法無法調(diào)用。
需要關(guān)閉ARC才可以調(diào)用。
2.內(nèi)存管理的原則
1). 有對象的創(chuàng)建,就要匹配1個(gè)release
2). retain的次數(shù),和release的次數(shù)要匹配。
3). 誰用誰retain,誰不用誰release。
誰負(fù)責(zé)retain,誰就負(fù)責(zé)release。
4). 只有在多1個(gè)人使用的時(shí)候,才retain。少1個(gè)人使用的時(shí)候,才release。
有始有終,有加就有減。有retain,就應(yīng)該匹配一個(gè)release。一定要平衡。
113. 野指針和僵尸對象。
1.野指針:
C語言中的野指針:
定義1個(gè)指針變量,沒有初始化。這個(gè)指針變量的值是1個(gè)垃圾值,指向1塊隨機(jī)的空間。這個(gè)指針叫做野指針。
OC 語言的野指針:
指針指向的對象已經(jīng)被回收了,這樣的指針就叫野指針。
2. 對象回收的本質(zhì):
內(nèi)存回收的本質(zhì):
申請1個(gè)變量,實(shí)際上就是想系統(tǒng)申請指定字節(jié)數(shù)的空間。這些空間系統(tǒng)就不會再分配給別人了。
當(dāng)變量被回收的時(shí)候,代表變量所占用的字節(jié)空間從此以后系統(tǒng)可以分配給別人使用了。
但是字節(jié)空間中,存儲的數(shù)據(jù)還在。
回收對象:
所謂對象的回收,指的是對象占用的空間可以分配給別人使用。
當(dāng)這個(gè)對象占用的空間,沒有分配給別人之前,其實(shí)對象數(shù)據(jù)還在。
3. 僵尸對象
1個(gè)已經(jīng)被釋放的對象,但是這個(gè)對象所占的空間還沒有分配給別人。這樣的對象,叫做僵尸對象。
我們通過野指針,去訪問僵尸對象的時(shí)候。有可能有問題,也可能沒有問題。
當(dāng)僵尸對象占用的空間,還沒有分配給別人使用的時(shí)候,這時(shí)是可以的。
當(dāng)僵尸對象占用的空間,分配給了別人使用的時(shí)候,就不可以了。
4. 我們認(rèn)為,只要對象變?yōu)榱私┦瑢ο螅瑹o論如何,都不允許訪問樂
如果訪問的是僵尸對象,無論如何報(bào)錯(cuò)。
僵尸對象的實(shí)時(shí)檢查機(jī)制。打開后,只要訪問的是僵尸對象,無論空間是否分配了,都報(bào)錯(cuò)。
Run---Diagnostics---Enable Zombie Objects。
5. 為什么不默認(rèn)打開僵尸對象監(jiān)測?
一旦打開僵尸對象監(jiān)測,那么每訪問1個(gè)對象的時(shí)候,都會先檢查這個(gè)對象是否為1個(gè)僵尸對象。
這樣機(jī)器消耗性能。
6. 使用野指針訪問僵尸對象,會報(bào)錯(cuò)。如何避免僵尸對象錯(cuò)誤。
將1個(gè)指針稱為野指針之后, 將這個(gè)指針的值設(shè)置為nil。
當(dāng)1個(gè)指針的值為nil,通過這個(gè)指針去調(diào)用對象的方法(包括使用點(diǎn)語法)的時(shí)候。不會報(bào)錯(cuò),只是沒有任何反應(yīng)。
但是如果通過直接訪問屬性 -> 就會報(bào)錯(cuò)。
7. 無法復(fù)活1個(gè)僵尸對象(retain)。
Person *p1 =[Person new]; // 1
[p1 release]; // 0
[p1 retain]; // 不能復(fù)活對象
114. 單個(gè)對象的內(nèi)存管理。
1.內(nèi)存泄漏:
指的是1個(gè)對象,沒有被及時(shí)的回收;在該回收的時(shí)候而沒有被回收。
一直駐留在內(nèi)存中,直到程序結(jié)束的時(shí)候才回收。
2.單個(gè)對象的內(nèi)存泄漏情況。
1). 有對象的創(chuàng)建,而沒有對應(yīng)的release。
2). retain的次數(shù)和release的次數(shù)不匹配。
3). 在不適當(dāng)?shù)臅r(shí)候,為指針賦值為nil。
4). 在方法當(dāng)中,為傳入的對象,進(jìn)行不適當(dāng)?shù)膔etain。
3. 如何保證單個(gè)對象可以被回收。。
1). 有對象的創(chuàng)建,就必須匹配1個(gè)release;
2). retain次數(shù) 和release次數(shù)一定要匹配。
3). 只有在指針稱為野指針的時(shí)候, 才賦值為nil。
4). 在方法中,不要隨意的為傳入的對象retain。
115. setter方法管理內(nèi)存。
@implementation Person
- (void)dealloc {
// 當(dāng)人對象掛的時(shí)候,代表當(dāng)前這個(gè)人不會再使用_car指向的對象了。
// 不在使用1個(gè)對象的,應(yīng)該為這個(gè)對象發(fā)送1條release消息。
[_car release];
NSLog(@"人掛了。。。");
[super dealloc];
}
- (void)setCar: (Car *)car {
// 將傳入的車對象,賦值給當(dāng)前對象的_car屬性。傳入的對象多了1個(gè)人使用。
// 那么就應(yīng)該先為這個(gè)對象,發(fā)送1條retain消息。代表多1個(gè)人使用。
_car = [car retain] ;
}
1. 當(dāng)屬性是1個(gè)OC對象的時(shí)候,setter方法的寫法。
將傳進(jìn)來的對象,賦值給當(dāng)前對象的屬性,代表傳入的對象多了1個(gè)人使用。所以應(yīng)該先為這個(gè)傳入的對象,發(fā)送1條retain消息,再賦值。
當(dāng)當(dāng)前對象銷毀的時(shí)候,代表屬性指向的對象少1個(gè)人使用。就應(yīng)該在dealloc中release。
代碼寫法:
- (void)dealloc {
[_car release];
[super dealloc];
}
- (void)setCar: (Car *)car {
_car = [car retain] ;
}
116. setter方法管理內(nèi)存2
重新賦值,代表原來的對象少1個(gè)人使用。
// Person.m
- (void)dealloc {
[_car release];
[super dealloc];
}
- (void)setCar: (Car *)car { // car對象替換,原來的對象可能存在內(nèi)存泄漏。
// _car 屬性原本只想的對象,少1個(gè)人使用;
// 傳入的對象多1個(gè)人使用。
[ _car release];
_car = [car retain] ;
}
2. 當(dāng)屬性是1個(gè)OC對象的時(shí)候,setter方法還是有bug。當(dāng)為對象的屬性多次賦值的時(shí)候。就會發(fā)生內(nèi)存泄漏。
發(fā)生內(nèi)存泄漏的原因:當(dāng)為屬性賦值的時(shí)候。代表舊對象少1個(gè)人用;新對象多1個(gè)人使用。
應(yīng)該release舊對象,retain新的對象。
- (void)setCar: (Car *)car {
[ _car release];
_car = [car retain] ;
}
117. setter方法3:
// Car.m
@implementation Car
- (void) dealloc {
NSLog(@"速度為%d的車子銷毀了。",_speed);
[super dealloc];
}
3. 出現(xiàn)僵尸對象錯(cuò)誤的原因,在于新舊對象是同1個(gè)對象。
解決方案:當(dāng)發(fā)現(xiàn)新舊對象是同1個(gè)對象,什么都不要做。
只有當(dāng)新舊對象,不是同1個(gè)對象的時(shí)候。才release舊的,retain新的。
- (void)setCar: (Car *)car {
if(_car != car) { // 說明新舊對象,不是同1個(gè)對象。
[ _car release]; // release舊的,retain新的。
_car = [car retain] ;
}
}
- (void)dealloc {
[ _car release];
[super dealloc];
}
4. 只有OC對象,才有release方法。
內(nèi)存管理的范圍,是OC對象,所以,只有屬性的類型是OC對象的時(shí)候,這個(gè)屬性的setter方法才要像上面那樣寫、
如果屬性不是OC對象類型的,setter方法直接賦值就可。
NSString 是OC對象的類型。
-(void) dealloc {
NSLog(@"人死了");
[ _car release];
[ _name release];
[super dealloc];
}
-(void) setName: (NSString *)name {
if(_name != name) {
[_name release];
_name = [name retain];
}
}