導(dǎo)讀
- 一、為什么要進行內(nèi)存管理
- 二拟赊、內(nèi)存管理機制
- 三痛垛、內(nèi)存管理原則
- 四草慧、MRC手動內(nèi)存管理
- 五、ARC自動內(nèi)存管理
- 六匙头、Autorelease自動釋放池
- 參考:http://www.reibang.com/p/7903c8283e26
一漫谷、 為什么要進行內(nèi)存管理
- 移動設(shè)備分配給每個App的內(nèi)存有限,App運行中會創(chuàng)建大量對象蹂析, OC對象存儲在堆中舔示,系統(tǒng)不會自動釋放堆中的內(nèi)存,對象沒有及時釋放电抚,就會占用大量內(nèi)存惕稻,系統(tǒng)會發(fā)出內(nèi)存警告,對應(yīng)用運行造成影響蝙叛。
- 相關(guān)概念:
內(nèi)存泄露:代碼塊結(jié)束時其內(nèi)部的所有局部變量會被回收俺祠,指向?qū)ο蟮闹羔樢脖换厥眨绻麑ο鬀]有指針指向,但依然存在于內(nèi)存中蜘渣,造成內(nèi)存泄露淌铐。
野指針錯誤:訪問了一塊壞的內(nèi)存(已經(jīng)被回收的,不可用的內(nèi)存)蔫缸。
僵尸對象:所占內(nèi)存已經(jīng)被回收的對象腿准,僵尸對象不能再被使用。(打開僵尸對象檢測)
空指針:沒有指向任何東西的指針(存儲的東西是0捂龄、null释涛、 nil),給空指針發(fā)送消息不會報錯倦沧。
二唇撬、內(nèi)存管理機制
- 在C#中都有GC在自動管理內(nèi)存,但是在OC中沒有垃圾回收機制展融,OC提供了一套機制來管理內(nèi)存窖认,即“引用計數(shù)”(retain counting):
- 每一個對象都有一個引用計數(shù)(retain count),對象被創(chuàng)建的時候告希,引用計數(shù)的值是1 當(dāng)調(diào)用對象的alloc扑浸、retain、new燕偶、copy方法之后引用計數(shù)器值自動在原來的基礎(chǔ)上加1喝噪,當(dāng)調(diào)用對象的release方法之后引用計數(shù)器值減1,調(diào)用autorelease 使對象的引用計數(shù)在未來的某個時候減1指么, 當(dāng)引用計數(shù)值是0的時候酝惧,對象將被銷毀。
- 在每個OC對象內(nèi)部伯诬,都專門有4個字節(jié)的存儲空間來存儲引用計數(shù)器晚唇。
內(nèi)存管理范圍:任何繼承NSObject的對象;OC中的基本類型存儲在棧上盗似,由系統(tǒng)進行管理哩陕。
內(nèi)存管理方法有:MRC(手動引用計數(shù))、ARC(自動引用計數(shù))赫舒、Autorelease(自動釋放池)悍及。
三、內(nèi)存管理原則
- 誰創(chuàng)建接癌, 誰release心赶。
如果你通過alloc、new扔涧、copy來創(chuàng)建了一個對象,那么你就必須調(diào)用release或者autorelease方法。不是你創(chuàng)建的就不用你去負(fù)責(zé)枯夜。 - 誰retain弯汰, 誰release。
只要你調(diào)用了retain湖雹,無論這個對象時如何生成的咏闪,你都要調(diào)用release。 - 除了alloc摔吏、new或copy之外的方法創(chuàng)建的對象在內(nèi)部都被聲明了autorelease鸽嫂,如[UIButton buttonWithType:]
- 在一定的代碼段內(nèi),對同一個對象所做的copy征讲、alloc和retain的操作次數(shù)應(yīng)當(dāng)與release和 autorelease操作的次數(shù)相等据某。
Apple官網(wǎng)內(nèi)存管理三定律
1. 一個對象可以有一個或多個擁有者
2. 當(dāng)對象一個擁有者都沒有時,就會被回收
3. 一個對象如果想保留不被回收,必須具有擁有者
四、MRC手動引用計數(shù)
- 在引入ARC(Automatic Reference Counting)自動引用計數(shù)機制之前诗箍,OC的內(nèi)存管理需要由開發(fā)人員手動維護即MRC癣籽。
- 在Xcode4.2之后的版本中??引入了ARC,程序編譯時滤祖,Xcode可以自動為你的代碼添加內(nèi)存釋放代碼筷狼,如果編寫手動釋放代碼Xcode會報錯,因此如果使用的Xcode4.2之后的版本匠童,必須手動關(guān)閉ARC埂材,這樣才有助于我們理解OC的內(nèi)存管理機制。
-
為了理解OC的內(nèi)存管理機制汤求,需要在Xcode中關(guān)閉ARC:項目屬性—Build Settings--搜索“garbage”找到Objective-C Automatic Reference Counting設(shè)置為No即可俏险。
(一) Set方法的代碼規(guī)范
- 基本數(shù)據(jù)類型:直接復(fù)制
-(void)setAge:(int)age
{
_age=age;
} - OC對象類型
-(void)setCar:(Car *)car
{
//先判斷是不是新傳進來的對象
If(car!=_car)
{
//對舊對象做一次release
[_car release];//若沒有舊對象,則沒有影響
//對新對象做一次retain
_car=[car retain];
}
}
(二) dealloc方法的代碼規(guī)范
- 一定要[super dealloc]首昔,而且要放到最后
- 對self(當(dāng)前)所擁有的的其他對象做一次release操作
-(void)dealloc
{
[_car release];
[super dealloc];
}
(三) 實例代碼規(guī)范
- WZKPerson.h
#import <Foundation/Foundation.h>
@interface WZKPerson : NSObject
@property(nonatomic,copy)NSString *name;
@property(nonatomic,assign)NSInteger age;
@end
WZKPerson.m
#import "WZKPerson.h"
@implementation WZKPerson
-(void)dealloc
{
self.name=nil;
/*最后一定要調(diào)用父類的dealloc方法寡喝;
目的:一是父類可能有其他引用對象需要釋放;二是當(dāng)前對象真正的釋放操作是在super的dealloc中完成的勒奇;
*/
[super dealloc];
}
@end-
main.m(部分代碼)
//調(diào)用alloc预鬓,引用計數(shù)+1
WZKPerson *personTest=[[WZKPerson alloc] init];
personTest.name=@"test";
personTest.age=30;//輸出personTest對象的引用計數(shù) NSLog(@"personTest的引用計數(shù):%lu",[personTest retainCount]); //輸出結(jié)果:personTest的引用計數(shù):1 //執(zhí)行personTest的dealloc方法 //調(diào)用過release方法之后,personTest指向的對象就會被銷毀赊颠,但是此時變量personTest中還存放著WZKPerson對象的地址 [personTest release]; //如果不設(shè)置personTest=nil格二,則personTest就是一個野指針,它指向的內(nèi)存不屬于這個程序竣蹦,非常危險 personTest=nil; //如果不設(shè)置personTest=nil顶猜,此時再調(diào)用personTest的release方法會報錯 //如果設(shè)置了personTest=nil,此時personTest已經(jīng)是空指針了痘括,則oc中給空指針發(fā)送消息是不會報錯的 [personTest release]; WZKPerson *personTest2=[[WZKPerson alloc] init]; personTest2.name=@"test2"; personTest2.age=30; //輸出結(jié)果:personTest的引用計數(shù):1 NSLog(@"personTest2的引用計數(shù):%lu",[personTest2 retainCount]); //引用計數(shù)+1 [personTest2 retain]; //輸出結(jié)果:personTest的引用計數(shù):2 NSLog(@"personTest2的引用計數(shù):%lu",[personTest2 retainCount]); //引用計數(shù)-1 [personTest2 release]; //輸出結(jié)果:personTest的引用計數(shù):1 NSLog(@"personTest2的引用計數(shù):%lu",[personTest2 retainCount]); //執(zhí)行personTest2的dealloc方法 [personTest2 release]; personTest2=nil;
在上述代碼中长窄,可以通過dealloc方法來查看是否一個對象已經(jīng)被回收滔吠,如果沒有回收,則有可能造成內(nèi)存泄漏挠日。
如果一個對象被釋放后疮绷,那么最后引用它的變量需要手動設(shè)置為nil,否則可能造成野指針錯誤嚣潜。
五冬骚、ARC內(nèi)存管理機制
(一) ARC的判斷準(zhǔn)則
- 只要沒有強指針指向,對象就會被釋放懂算。
(二) 指針分類
- 強指針:默認(rèn)的情況下只冻,所有的指針都是強指針,關(guān)鍵字strong
- 弱指針:_ _weak關(guān)鍵字修飾的指針
- 聲明一個弱指針如下:
_ _weak Person *p;
//ARC中计技,只要弱指針指向的對象不在了喜德,就直接把弱指針做清空操作。
_ _weak Person *p=[[Person alloc] init];
//不合理酸役,對象一創(chuàng)建出來就被釋放掉住诸,對象釋放掉后,ARC把指針自動清零涣澡。 - ARC中在property處不再使用retain贱呐,而是使用strong,在dealloc中不需要再[super dealloc]
@property(nonatomic,strong)Dog *dog;
// 意味著生成的成員變量dog是一個強指針入桂,相當(dāng)于以前的retain奄薇。
//如果換成是弱指針,則換成weak抗愁,不需要加 _馁蒂。
(三) ARC的特點總結(jié)
- 不允許調(diào)用release、retain蜘腌、retainCount
- 允許重寫dealloc沫屡,但是不允許調(diào)用[super dealloc]
- @property的參數(shù):
Strong:相當(dāng)于原來的retain(適用于OC對象類型),成員變量是強指針
Weak:相當(dāng)于原來的assign(適用于oc對象類型)撮珠,成員變量是弱指針
Assign:適用于非OC對象類型(基礎(chǔ)類型)
(四) 補充
- 讓程序兼容ARC和非ARC部分沮脖。轉(zhuǎn)變?yōu)榉茿RC -fno-objc-arc 轉(zhuǎn)變?yōu)锳RC的, -f-objc-arc 芯急。
-
ARC也需要考慮循環(huán)引用問題:一端使用retain勺届,另一端使用assign。
- 提示:字符串是特殊的對象娶耍,但不需要使用release手動釋放免姿,這種字符串對象默認(rèn)就是autorelease的,不用額外的去管內(nèi)存榕酒。
六胚膊、 Autorelease自動釋放池
- 在OC中存在著一種內(nèi)存自動釋放機制叫做自動釋放池(或自動引用計數(shù))故俐,但是與C#不同的是,這僅僅是一種半自動的機制紊婉,有些操作還是需要進行手動設(shè)置购披。
(一) 下面通過代碼來了解一下自動釋放池
- WZKPerson.h
//構(gòu)造函數(shù)
-(WZKPerson *)initWithName:(NSString *)name age:(NSInteger)age;
//獲取對象的類方法
+(WZKPerson *)personWithName:(NSString *)name;
-
WZKPerson.m
-(WZKPerson *)initWithName:(NSString *)name age:(NSInteger)age
{
self=[super init];
if (self) {
_name=[name copy];
_age=age;
}
return self;
}+(WZKPerson *)personWithName:(NSString *)name { //這里調(diào)用了autorelease //OC類庫中的類方法一般都不需要手動釋放,內(nèi)部已經(jīng)調(diào)用了autorelease方法肩榕; WZKPerson *person=[[[WZKPerson alloc] init] autorelease]; return person; }
-
main.m(部分代碼)
int main(int argc, const char * argv[]) {
@autoreleasepool {
WZKPerson *person1=[[WZKPerson alloc] init];
//調(diào)用autorelease方法,后面就不需要手動調(diào)用release方法了
[person1 autorelease];
//由于autorelease是延遲釋放(延遲到自動釋放池銷毀)惩妇,//所以這里仍然可以使用person1對象 person1.name=@"Kevin"; //調(diào)用autorelease方法 WZKPerson *person2=[[[WZKPerson alloc] initWithName:@"Kevin" age:27] autorelease]; //內(nèi)部已經(jīng)調(diào)用了autorelease株汉,所以不需要手動釋放 //另外由于內(nèi)存管理原則,在外部不使用alloc歌殃、new乔妈、copy操作, //就不需要調(diào)用release或autorelease氓皱,所以這個操作是放到類方法內(nèi)部進行完成 WZKPerson *person3=[WZKPerson personWithName:@"Kevin"]; } return 0; }
(二) 自動內(nèi)存釋放總結(jié)
1. 基本用法
- 自動內(nèi)存釋放使用@autoreleasepool關(guān)鍵字聲明一個代碼塊路召,如果一個對象在初始化時調(diào)用了autorelease方法,會將這個對象放到位于棧頂?shù)尼尫懦刂小?/li>
- 當(dāng)代碼塊執(zhí)行完之后即當(dāng)自動釋放池被銷毀時波材,在塊中調(diào)用過autorelease方法的對象都會自動調(diào)用一次release方法股淡, 但是不一定能夠銷毀對象,例如:當(dāng)對象引用計數(shù)器值大于1時廷区,該對象就無法銷毀唯灵。
- OC中類庫的類方法一般都不需要手動釋放,因為內(nèi)部已經(jīng)調(diào)用了autorelease方法隙轻。
2. 好處
- 不需要再關(guān)心對象釋放的時間
- 不需要再關(guān)心什么時候調(diào)用release
3. 使用注意
- 由于自動釋放池最后統(tǒng)一銷毀對象埠帕,因此如果一個操作比較占用內(nèi)存,最好不要放到自動釋放池或者放到多個自動釋放池玖绿;應(yīng)該使用release來精確控制
- 占用內(nèi)存較小的對象使用autorelease敛瓷,沒有太大的影響。
- autorelease方法不會改變對象的引用計數(shù)器(銷毀時影響)斑匪,只是將這個對象放到自動釋放池中呐籽;
- 系統(tǒng)自帶的方法中,如果不包含alloc秤标、 new 绝淡、copy等,則這些方法返回的對象都是autorelease的苍姜,如[NSDate date]牢酵。
- 開發(fā)中經(jīng)常會寫一些類方法來快速創(chuàng)建一個autorelease對象,創(chuàng)建對象時不要直接使用類名衙猪,而是使用self馍乙。
4. 錯誤寫法
- 連續(xù)調(diào)用多次autorelease布近,釋放池銷毀時執(zhí)行兩次release(-1嗎?)
- Alloc之后調(diào)用了autorelease丝格,之后又調(diào)用了release撑瞧。