一.基本介紹(先創(chuàng)建一個空工程待用)
1)為什么要管理內(nèi)存?
因為手機內(nèi)存大小有限,如果有內(nèi)存分配但是不釋放它,哪怕這塊內(nèi)存已經(jīng)不用了.導(dǎo)致你的應(yīng)用程序占用越來越多的內(nèi)存,并導(dǎo)致整體性能下降,或者直接在真機上閃退.因此需要把不必要的內(nèi)存空間給釋放掉.
2)OC內(nèi)存管理的三種方式
Objective-C提供了三種內(nèi)存管理方式:
1)manual reference counting(MRC,手動管理)
2)automatic reference counting(ARC,自動引用計數(shù))
3)garbage collection(GC,垃圾回收).OS X 10.8(山獅)被廢棄,使用ARC
IOS不支持垃圾回收雾袱;ARC作為蘋果,在XCode4.2以后(IOS5)新提供的技術(shù),蘋果推薦開發(fā)者使用ARC技術(shù)來管理內(nèi)存穗慕;但是我們從手動管理開始學(xué)起.
3)新創(chuàng)建的工程默認都是ARC(自動管理),修改為手動管理
1)選中藍色的項目工程文件
2)中間部分找到build settings選項
3)搜索gar,把那個選項改為NO
4). 怎么知道創(chuàng)建的對象被釋放?(畫圖來說明:人和牛的例子)
每個OC的對象都具備一個屬性叫做retainCount(該對象被多少個人套住,有多少人在使用),如果這個屬性>=1,意味著至少有一個人再使用,只有當(dāng)retainCount變?yōu)?,蘋果自動釋放
testOne:
People * zhangSan = [[People alloc]init];
NSLog(@"1樱蛤、引用計數(shù) = %d",zhangSan.retainCount);
[zhangSan release];
NSLog(@“2渣玲、引用計數(shù) = %d",zhangSan.retainCount);
//重寫People的dealloc方法,證明被釋放
-(void)dealloc{
NSLog(@"Invoke Person's dealloc method.”);
[super dealloc];
//注意最后一定要調(diào)用父類的dealloc方法(兩個目的:一是父類可能有其他引用對象需要釋放稿茉;二是:當(dāng)前對象真正的釋放操作是在super的dealloc中完成的)
}
總結(jié)
1.alloc/new會導(dǎo)致引用計數(shù)+1
2.release 會導(dǎo)致引用計數(shù)-1
3.對象在使用完成之后不會自動銷毀,如果不釋放會內(nèi)存泄露
4.已經(jīng)釋放的數(shù)據(jù)再次使用/釋放(過度釋放)會崩潰,通常會出現(xiàn)Thread 1:EXC_BAD_ACCESS
'(code=EXC_I386_GPFLT)錯誤,這是野指針錯誤,因為你訪問了一塊已經(jīng)不屬于你的內(nèi)存
解決辦法:[obj release]; obj = nil;因為給空對象發(fā)送消息是不會引起錯誤的
5.對象被釋放的時候會調(diào)用自己類的delloc方法
testTwo:
People * zhangSan = [[People alloc]init];
NSLog(@"1遍坟、引用計數(shù) = %d",zhangSan.retainCount);
[zhangSan retain];
NSLog(@"2、引用計數(shù) = %d",zhangSan.retainCount);
[zhangSan retain];
NSLog(@"3资盅、引用計數(shù) = %d",zhangSan.retainCount);
[zhangSan release];//alloc
[zhangSan release];//retain
[zhangSan release];//retain
總結(jié):
1.retain會導(dǎo)致引用計數(shù)+1
2.誰創(chuàng)建誰釋放,誰使用誰管理
3.引用計數(shù)+1的操作稱為”擁有take ownership of”,-1的操作稱為”放棄relinquish”,只有在擁有對象的時候才可以放心的使用,當(dāng)它被所有人放棄的時候Objective-C運行環(huán)境會自動回收這個對象
testThree(給People類擴充一個myLabel屬性,一個age屬性)
People * p = [People new];
UILabel * a = [UILabel new];
NSLog(@"a 的引用計數(shù)為”,a.retainCount);
p.myLabel = a;
NSLog(@"a 的引用計數(shù)為”,a.retainCount);
[a release];
NSLog(@"a 的引用計數(shù)為”,a.retainCount);
[p release];
總結(jié):
1.給屬性賦值,會導(dǎo)致引用計數(shù)+1
2.對于類中聲明的屬性一般在delloc方法中釋放,釋放后記得=nil
3.對于assign修飾的屬性不需要內(nèi)存管理,因為內(nèi)存管理是針對于對象的.
testFour:
People * p1 = [People new];
People * p2 = p1;
NSLog(@"p1的引用計數(shù)為”,p1.retainCount);
NSLog(@"p2的引用計數(shù)為”,p2.retainCount);
[p1 retain];
NSLog(@"p1的引用計數(shù)為”,p1.retainCount);
NSLog(@"p2的引用計數(shù)為”,p2.retainCount);
結(jié)論:
1.指針賦值不會讓引用計數(shù)+1
2.p2的引用計數(shù)等于p1的,p1改p2也改
testOne
UILabel * label = [UILabel new];
NSLog(@"label的引用計數(shù)為”, label.retainCount);
[self.window addSubView:label];
NSLog(@"label的引用計數(shù)為”, label.retainCount);
[label release];
結(jié)論:
1.addSubView會導(dǎo)致引用計數(shù)+1,因為它被self.window擁有,遵循誰擁有誰管理原則,它內(nèi)部會自己處理內(nèi)存問題,不用我們釋放
2.什么時候release?當(dāng)你確定該對象不會被使用了,就可以release,一般寫在最后.
testTwo(脫兩個xib控件并且綁定對象)
NSLog(@"a %d”,a.retainCount);
NSLog(@"b %d”,b.retainCount);
結(jié)論:
1.通過拖拽xib,引用計數(shù)+1,原理同結(jié)論1,而且系統(tǒng)會在delloc中自己釋放
2.全局變量一般在delloc中釋放,局部變量用完就release
3.一般來說,區(qū)間1和區(qū)間2的對象屬性都在delloc方法中釋放
testThree
-(void)textFout {
UILabel * label = [[self createLabel] retain];
[label release];
label = nil;
}
-(UILabel *)createUILabel {
UILabel * label = [UILabel new];
******做一些其他事情*****
return [label autorelease];
}
結(jié)論
1.autorelease自動釋放
* 對象執(zhí)行autorelease方法時會將對象添加到自動釋放池(autorelease pool)中
* 當(dāng)自動釋放池銷毀時自動釋放池中所有對象作release操作,如果引用計數(shù)變?yōu)?就釋放
2.一般來說,如果方法最后要返回一個對象,一般需要使用autorelease去釋放
3.release 和 autorelease 的區(qū)別?
* 如果能確定,使用release
* 不能確定,使用autorelease
注:autorelease不是隨便用的
* 操作占用內(nèi)存比較大的對象的時候不要隨便使用,擔(dān)心對象釋放的時間太遲,可能會泄露
* 操作占用內(nèi)存比較小的對象可以使用
* 自動釋放池存儲于內(nèi)存中的棧中遵循"先進后出”原則
* 自動釋放池實質(zhì)是當(dāng)自動釋放池銷毀后調(diào)用對象的release方法,不一定就能銷毀對象(例如如果一個對象的引用計數(shù)器>1則此時就無法銷毀)
testFour
UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
NSLog(@“btn",btn.retainCount);
NSString * name = @"zhanSan";
NSLog(@" name",name.retainCount);
NSString * name1 = [NSString stringWithFormat:@“zhangSan"];
NSLog(@" name",name.retainCount);
結(jié)論:
1.ObjC中類庫中的+號方法(靜態(tài)方法)創(chuàng)建對象一般都不需要手動釋放,內(nèi)部已經(jīng)調(diào)用了autorelease方法;
2.字符串是一個比較特殊的對象,但是只要人為+1,就要人為-1
testFive
UILabel * label = [UILabel new];
NSLog(@"%d",label.retainCount);
NSArray * array = [NSArray arrayWithObjects:label,label, nil];
NSLog(@"array:%d",array.retainCount);
NSLog(@"%d",label.retainCount);
[label release];//new
結(jié)論:
1.數(shù)組的addObject會讓對象的引用計數(shù)+1.
數(shù)組在釋放的時候,會對里面的對象執(zhí)行release,所以不用我們管.
testSix
導(dǎo)航傳值導(dǎo)致引用計數(shù)+1
界面?zhèn)髦等菀壮鲥e(記得全局變量到delloc中釋放)
* _oneArray = [array retain];
* self.oneArray = array;
test.png
@property的參數(shù)分為三類调榄,也就是說參數(shù)最多可以有三個,中間用逗號分隔呵扛,每類參數(shù)可以從上表三類參數(shù)中人選一個每庆。如果不進行設(shè)置或者只設(shè)置其中一類參數(shù),程序會使用三類中的各個默認參數(shù)今穿,默認參數(shù):(atomic,readwrite,assign)
一般情況下如果在多線程開發(fā)中一個屬性可能會被兩個及兩個以上的線程同時訪問缤灵,此時可以考慮atomic屬性,否則建議使用nonatomic蓝晒,不加鎖腮出,效率較高;readwirte方法會生成getter芝薇、setter兩個方法胚嘲,如果使用readonly則只生成getter方法;關(guān)于set方法處理需要特別說明洛二,假設(shè)我們定義一個屬性a馋劈,這里列出三種方式的生成代碼:
assign,用于基本數(shù)據(jù)類型
-(void)setA:(int)a{
_a=a;
}
retain晾嘶,通常用于非字符串對象
-(void)setA:(Car *)a{
if(_a!=a){
[_a release];
_a=[a retain];
}
}
copy妓雾,通常用于字符串對象
-(void)setA:(NSString *)a{
if(_a!=a){
[_a release];
_a=[a copy];
}
}
內(nèi)存測試
NSArray * array1 = [NSArray arrayWithObjects:@"1", nil];
self.array4 = array1;
NSArray * array2 = [[NSArray alloc] initWithObjects:@"array1",array1, nil];
self.array4 = array2;
NSArray * array3 = [NSArray arrayWithObjects:array1,array2, nil];
self.array4 = array3;
//3--2--2--2
內(nèi)存管理的知識點
1.讓對象引用計數(shù)增加的9個操作
alloc,new,copy,retain,addSubView,addObject,push,對象打點屬性賦值,控件關(guān)聯(lián)xib
2.引用計數(shù)減少的兩個操作
release autorelease
3.要保證讓對象引用計數(shù)增加的關(guān)鍵字跟讓對象引用計數(shù)減少的關(guān)鍵字 配對
4.全局變量或者屬性在dealloc中釋放,局部變量在確定不使用以后就要釋放
5.使用+號方法(靜態(tài)方法)創(chuàng)建的對象不需要管理內(nèi)存,內(nèi)部自動-1
6.在方法中創(chuàng)建的對象如果要返回需要使用autorelease,在使用的時候手動retain
7.全局對象變量在初始化的時候一般使用-號方法創(chuàng)建,使用+號方法創(chuàng)建需要手動retain,使用字面量創(chuàng)建需要手動retain
8.分線程中的所有代碼都用@autorelease括起來,防止內(nèi)存泄露
9.對象在釋放的時候除了[obj release];以外,為了安全起見,加上obj=nil;
10.屬性直接self.obj = nil;即可,相當(dāng)于[obj release];obj=nil;
ARC和MRC的關(guān)系
ARC是編譯器特性,不是運行時特性.簡單說就是代碼在編譯的時候自動加入了retain/release/autorelease,原先需要手動添加的用來處理內(nèi)存管理的引用計數(shù)的代碼可以自動地由編譯器完成了.因此ARC和MRC性能是一樣的,有些時候還能更快,因為編譯器還可以執(zhí)行某些優(yōu)化
內(nèi)存管理的小技巧
1)利用你靜態(tài)分析工具讓系統(tǒng)自動檢測內(nèi)存泄露的代碼(只能檢測一部分)
1.選中項目工程文件
2.中間部分,選擇藍色的project
3.選中build settings,搜索Analyze During
4.把Analyze During ’Build’修改為YES
2)
strong weak
retain,copy assign
strong:強引用,只要對象有人在用就不會被釋放掉
weak:弱引用,并不是對象的真正的擁有者,如果對象被釋放,指針自動置為nil
為了方便釋放對象,定義一個簡單的宏定義
#define Release(obj) [obj release];obj=nil;
循環(huán)引用
1.定時器導(dǎo)致的循環(huán)引用
定時器在內(nèi)部會引用self,導(dǎo)致self在釋放的時候并不會釋放掉,因此在釋放對象之前需要先把定時器給關(guān)閉掉.釋放的代碼[_timer invalidate];_timer = nil;在delloc方法中調(diào)用無效,一般在viewDidDisappear中關(guān)閉定時器.
2.代理導(dǎo)致的循環(huán)引用 A,B,C
在A中創(chuàng)建B和C,引用計數(shù)分別為:A1,B1,A.delegate = B,引用計數(shù)變?yōu)?A1,B2.如果A釋放,B釋放,那么B變?yōu)锳,導(dǎo)致A.delegate也是1,所以A釋放不掉,B也沒釋放掉.
解決辦法就是:delegate用assign修飾
3.block導(dǎo)致的循環(huán)引用