標籤:
obj-c本質就是"改進過的c語言",大家都知道c語言是沒有記憶體回收(GC)機制的(註:雖然obj-c2.0後來增加了GC功能,但是在iphone上不能用,因此對於iOS平台的程式員來講,這個幾乎沒啥用),
所以在obj-c中寫程式時,對於資源的釋放得由開發人員手動處理,相對要費心一些。引用計數這是一種古老但有效記憶體管理方式。每個對象(特指:類的執行個體)內部都有一個retainCount的引用計數,對象剛被建立時,retainCount為1,可以手動調用retain方法使retainCount+1,
同樣也可以手動調用release方法使retainCount-1,調用release方法時,如果retainCount值減到0,系統將自動調用對象的dealloc方法(類似於c#中的dispose方法),
開發人員可以在dealloc中釋放或清理資源。1、基本用法為了示範這種基本方式,先定義一個類Sample類介面部分Sample.h//// Sample.h// MemoryManage_1//// Created by jimmy.yang on 11-2-19.// Copyright 2011 __MyCompanyName__. All rights reserved.// #import <Foundation/Foundation.h> @interface Sample : NSObject { } @end類實現部分Sample.m//// Sample.m// MemoryManage_1//// Created by jimmy.yang on 11-2-19.// Copyright 2011 __MyCompanyName__. All rights reserved.// #import "Sample.h" @implementation Sample -(id) init{ if (self=[super init]){ NSLog(@"建構函式被調用了!當前引用計數:%d",[self retainCount]); } return (self);} -(void) dealloc{ NSLog(@"解構函式將要執行...,當前引用計數:%d",[self retainCount]); [super dealloc];}@end代碼很簡單,除了"建構函式"跟"解構函式"之外,沒有任何其它多餘處理。主程式調用#import <Foundation/Foundation.h>#import "Sample.h" int main (int argc, const char * argv[]) { Sample *_sample = [Sample new]; //建構函式被調用了!當前引用計數:1 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//1 [_sample retain]; NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//2 [_sample retain]; NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//3 [_sample release]; NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//2 [_sample release]; NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//1 [_sample release];//解構函式將要執行...,當前引用計數:1 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//1,註:即便是在解構函式執行後,如果立即再次引用對象的retainCount,
仍然返回1,但以後不管再試圖引用該對象的任何屬性或方法,都將報錯 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//對象被釋放之後,如果再嘗試引用該對象的任何其它方法,則報錯 //[_sample retain];//同上,會報錯 return 0; }這段代碼主要驗證:對象剛建立時retainCount是否為1,以及retain和release是否可以改變retainCount的值,同時retainCount減到0時,是否會自動執行dealloc函數nil 的問題:1.1 如果僅聲明一個Sample類型的變數(其實就是一個指標),而不執行個體化,其初始值為nil1.2 變數執行個體化以後,就算release掉,dealloc被成功調用,其retainCount並不馬上回到0(還能立即調用一次且僅一次[xxx retainCount]),而且指標變數本身也不會自動歸為nil值1.3 dealloc被調用後,必須手動賦值nil,retainCount才會自動歸0以上結論是實際實驗得出來的,見下面的代碼:Sample *s ; NSLog(@"s %@,retainCount=%d",[email protected]"is nil":@"is not nil",[s retainCount]);//s is nil,retainCount=0 s = [Sample new];NSLog(@"s %@,retainCount=%d",[email protected]"is nil":@"is not nil",[s retainCount]);//s is not nil,retainCount=1 [s release];NSLog(@"s %@,retainCount=%d",[email protected]"is nil":@"is not nil",[s retainCount]);//s is not nil,retainCount=1//NSLog(@"s %@,retainCount=%d",[email protected]"is nil":@"is not nil",[s retainCount]);//報錯:Program received signal: “EXC_BAD_ACCESS”.s = nil;NSLog(@"s %@,retainCount=%d",[email protected]"is nil":@"is not nil",[s retainCount]);//s is nil,retainCount=0所以千萬別用if (x == nil) 或 if ([x retainCount]==0)來判斷對象是否被銷毀,除非你每次銷毀對象後,手動顯式將其賦值為nil2、複雜情況上面的樣本過於簡章,只有一個類自己獨耍,如果有多個類,且相互之間有聯絡時,情況要複雜一些。下面我們設計二個類Shoe和Man(即“鞋子類”和”人“),每個人都要穿鞋,
所以Man與Shoe之間應該是Man擁有Shoe的關係。Shoe.h介面定義部分#import <Foundation/Foundation.h> @interface Shoe : NSObject { NSString* _shoeColor; int _shoeSize;} //鞋子尺寸-(void) setSize:(int) size;-(int) Size; //鞋子顏色-(void) setColor:(NSString*) color;-(NSString*) Color; //設定鞋子的顏色和尺碼-(void) setColorAndSize:(NSString*) pColor shoeSize:(int) pSize; @endShoe.m實現部分//// Shoe.m// MemoryManage_1//// Created by jimmy.yang on 11-2-19.// Copyright 2011 __MyCompanyName__. All rights reserved.// #import "Shoe.h" @implementation Shoe //建構函式-(id)init{ if (self=[super init]){ _shoeColor = @"black"; _shoeSize = 35; } NSLog(@"一雙 %@ %d碼 的鞋子造好了!",_shoeColor,_shoeSize); return (self);} -(void) setColor:(NSString *) newColor{ _shoeColor = newColor;} -(NSString*) Color{ return _shoeColor;} -(void) setSize:(int) newSize{ _shoeSize = newSize;} -(int) Size{ return _shoeSize;} -(void) setColorAndSize:(NSString *)color shoeSize:(int)size{ [self setColor:color]; [self setSize:size];} //解構函式-(void) dealloc{ NSLog(@"%@ %d碼的鞋子正在被人道毀滅!",_shoeColor,_shoeSize); [super dealloc];} @endMan.h定義部分//// Man.h// MemoryManage_1//// Created by jimmy.yang on 11-2-20.// Copyright 2011 __MyCompanyName__. All rights reserved.// #import <Foundation/Foundation.h>#import "Shoe.h" @interface Man : NSObject { NSString *_name; Shoe *_shoe;} -(void) setName:(NSString*) name;-(NSString*)Name; -(void) wearShoe:(Shoe*) shoe;@endMan.m實現部分//// Man.m// MemoryManage_1//// Created by jimmy.yang on 11-2-20.// Copyright 2011 __MyCompanyName__. All rights reserved.// #import "Man.h" @implementation Man //建構函式-(id)init{ if (self=[super init]){ _name = @"no name"; } NSLog(@"新人\"%@\"出生了!",_name); return (self);} -(void) setName:(NSString *)newName{ _name =newName;} -(NSString*)Name{ return _name;} -(void) wearShoe:(Shoe *)shoe{ _shoe = shoe;} //解構函式-(void) dealloc{ NSLog(@"\"%@\"要死了! ",_name); [super dealloc];} @endmain函數調用#import <Foundation/Foundation.h>#import "Shoe.h"#import "Man.h" int main (int argc, const char * argv[]) { Man *jimmy = [Man new]; [jimmy setName:@"Jimmy"]; Shoe *black40 =[Shoe new]; [black40 setColorAndSize:@"Black" shoeSize:40]; [jimmy wearShoe:black40]; [jimmy release]; [black40 release]; return 0; }2011-02-23 13:05:50.550 MemoryManage[253:a0f] 新人"no name"出生了! 2011-02-23 13:05:50.560 MemoryManage[253:a0f] 一雙 black 35碼 的鞋子造好了! 2011-02-23 13:05:50.634 MemoryManage[253:a0f] "Jimmy"要死了! 2011-02-23 13:05:50.636 MemoryManage[253:a0f] Black 40碼的鞋子正在被人道毀滅!以上是輸出結果,一切正常,jimmy與black40佔用的資源最終都得到了釋放。但是有一點不太合理,既然鞋子(black40)是屬於人(jimmy)的,為什麼人死了(即:[jimmy release]),
卻還要main函數來責任燒掉他的鞋子?(即:main函數中還是單獨寫一行[black40 release]) 貌似人死的時候,就連帶自上的所有東西一併帶走,這樣更方便吧。ok,我們來改造一下Man.m中的dealloc()方法,改成下面這樣:+ View Code即:在Man被銷毀的時候,先把_shoe給銷毀。這樣在main()函數中,就不再需要單獨寫一行[black40 release]來釋放black40了.現在又有新情況了:jimmy交了一個好朋友mike,二人成了鐵哥們,然後jimmy決定把自己的鞋子black40,跟mike共同擁有,於是main函數就成了下面這樣:+ View Code麻煩來了:jimmy在掛掉的時候(即[jimmy release]這一行),已經順手把自己的鞋子也給銷毀了(也許他忘記了mike也在穿它),然後mike在死的時候,準備燒掉自已的鞋子black40,
卻被告之該對象已經不存在了。於是程式運行報錯:Running…2011-02-23 13:38:53.169 MemoryManage[374:a0f] 新人"no name"出生了!2011-02-23 13:38:53.176 MemoryManage[374:a0f] 一雙 black 35碼 的鞋子造好了!2011-02-23 13:38:53.177 MemoryManage[374:a0f] 新人"no name"出生了!2011-02-23 13:38:53.179 MemoryManage[374:a0f] "Jimmy"要死了! 2011-02-23 13:38:53.181 MemoryManage[374:a0f] Black 40碼的鞋子正在被人道毀滅!2011-02-23 13:38:53.183 MemoryManage[374:a0f] "mike"要死了! Program received signal: “EXC_BAD_ACCESS”.sharedlibrary apply-load-rules all(gdb)上面紅色的部分表示程式出錯了:Bad_Access也就是說訪問不存在的地址。最解決的辦法莫過於又回到原點,Man.m的dealloc中不連帶釋放Shoe執行個體,然後把共用的鞋子放到main函數中,等所有人都掛掉後,
最後再銷毀Shoe執行個體,但是估計main()函數會有意見了:你們二個都死了,還要來麻煩我料理後事。舉這個例子無非就是得出這樣一個原則:對於new出來的對象,使用retain造成的影響一定要運用相應的release抵消掉,
反之亦然,否則,要麼對象不會被銷毀,要麼過早銷毀導致後面的非法引用而出錯。
(轉)IOS記憶體管理 retain release