在OC中對象是存儲在堆中的婿脸,系統(tǒng)并不會自動釋放堆中的內(nèi)存粱胜,如果一個對象創(chuàng)建并使用后沒有得到及時釋放那么就會占用大量內(nèi)存。其他高級語言如C#狐树、Java都是通過垃圾回收來(GC)解決這個問題的焙压,但在OjbC中并沒有類似的垃圾回收機制,因此它的內(nèi)存管理就需要由開發(fā)人員手動維護抑钟。
引用計數(shù)器
在Xcode4.0之后涯曲,引入了ARC(Automatic Reference Counting)機制,程序編譯時Xcode可以自動給你的代碼添加內(nèi)存釋放代碼,如果編寫手動釋放代碼Xcode會報錯在塔,因此在今天的內(nèi)容中如果你使用的是Xcode4.2之后的版本(相信現(xiàn)在大部分朋友用的版本都比這個要高)幻件,必須手動關(guān)閉ARC,這樣才有助于你理解ObjC的內(nèi)存回收機制蛔溃。
Xcode關(guān)閉ARC:選中工程—>Build Settings-->輸入garbage找到Objective-C Automatic Reference Counting設(shè)置為No
在ObjC中內(nèi)存的管理是依賴對象引用計數(shù)器來進行的:在ObjC中每個對象內(nèi)部都有一個與之對應(yīng)的整數(shù)(retainCount)绰沥,叫“引用計數(shù)器”,當一個對象在創(chuàng)建之后它的引用計數(shù)器為1贺待,當調(diào)用這個對象的alloc徽曲、retain、new麸塞、copy方法之后引用計數(shù)器自動在原來的基礎(chǔ)上加1(ObjC中調(diào)用一個對象的方法就是給這個對象發(fā)送一個消息)秃臣,當調(diào)用這個對象的release方法之后它的引用計數(shù)器減1,如果一個對象的引用計數(shù)器為0哪工,則系統(tǒng)會自動調(diào)用這個對象的dealloc方法來銷毀這個對象奥此。
代碼如下:
#import <Foundation/Foundation.h>
#import "Person.h"
void Test1(){
Person *p=[[Person alloc]init]; //調(diào)用alloc弧哎,引用計數(shù)器+1 p.name=@"Kenshin"; p.age=28;
NSLog(@"retainCount=%lu",[p retainCount]);
//結(jié)果:
retainCount=1 [p release]; //結(jié)果:Invoke Person's dealloc method.
//上面調(diào)用過release方法,p指向的對象就會被銷毀得院,但是此時變量p中還存放著Person對象的地址傻铣,
//如果不設(shè)置p=nil,則p就是一個野指針祥绞,它指向的內(nèi)存已經(jīng)不屬于這個程序非洲,因此是很危險的 p=nil;
//如果不設(shè)置p=nil,此時如果再調(diào)用對象release會報錯蜕径,但是如果此時p已經(jīng)是空指針了两踏,
//則在ObjC中給空指針發(fā)送消息是不會報錯的
[p release];
}
void Test2(){
Person *p=[[Person alloc]init];
p.name=@"Kenshin";
p.age=28;
NSLog(@"retainCount=%lu",[p retainCount]);
//結(jié)果:
retainCount=1 [p retain];//引用計數(shù)器+1
NSLog(@"retainCount=%lu",[p retainCount]);
//結(jié)果:
retainCount=2 [p release];//調(diào)用1次release引用計數(shù)器-1 NSLog(@"retainCount=%lu",[p retainCount]);
//結(jié)果:
retainCount=1 [p release];
//結(jié)果:Invoke Person's dealloc method. p=nil;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
Test1();
}
return 0;
}
注意:release會使引用計數(shù)減1,當引用計數(shù)為0的時候兜喻,系統(tǒng)會自動調(diào)用dealloc銷毀對象梦染,在對象release之后,需要將對象置nil朴皆,否則會成為野指針帕识,在OC中,對一個空的對象發(fā)送消息是不會引起錯誤的遂铡。
內(nèi)存釋放的原則就是:誰分配肮疗,誰釋放
自動釋放池
這只是一種半自動的機制,有些操作還是需要我們手動設(shè)置的扒接。自動內(nèi)存釋放使用@autoreleasepool關(guān)鍵字聲明一個代碼塊伪货,如果一個對象在初始化時調(diào)用了autorelase方法,那么當代碼塊執(zhí)行完之后钾怔,在塊中調(diào)用過autorelease方法的對象都會自動調(diào)用一次release方法碱呼。這樣一來就起到了自動釋放的作用,同時對象的銷毀過程也得到了延遲(統(tǒng)一調(diào)用release方法)宗侦。
代碼如下:
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person1=[[Person alloc]init];
[person1 autorelease];//調(diào)用了autorelease方法后面就不需要手動調(diào)用release方法了
person1.name=@"Kenshin";//由于autorelease是延遲釋放愚臀,所以這里仍然可以使用
person1 Person *person2=[[[Person alloc]initWithName:@"Kaoru"] autorelease];//調(diào)用了autorelease方法
Person *person3=[Person personWithName:@"rosa"];//內(nèi)部已經(jīng)調(diào)用了autorelease,所以不需要手動釋放矾利,這也符合內(nèi)存管理原則姑裂,因為這里并沒有alloc所以不需要release或者autorelease
Person *person4=[Person personWithName:@"jack"];
[person4 retain];
}
/*結(jié)果:
Invoke Person(rosa) dealloc method.
Invoke Person(Kaoru) dealloc method.
Invoke Person(Kenshin) dealloc method.
*/
return 0;
}
對于自動內(nèi)存釋放簡單總結(jié)一下:
(1)autorelease方法不會改變對象的引用計數(shù)器,只是將這個對象放到自動釋放池中梦皮;
(2)自動釋放池實質(zhì)是當自動釋放池銷毀后調(diào)用對象的release方法炭分,不一定就能銷毀對象(例如如果一個對象的引用計數(shù)器>1則此時就無法銷毀);
(3)由于自動釋放池最后統(tǒng)一銷毀對象剑肯,因此如果一個操作比較占用內(nèi)存(對象比較多或者對象占用資源比較多)捧毛,最好不要放到自動釋放池或者考慮放到多個自動釋放池;
(4)ObjC中類庫中的靜態(tài)方法一般都不需要手動釋放,內(nèi)部已經(jīng)調(diào)用了autorelease方法呀忧;