首先先解釋為什麼變數有時候要加星號(*),有時候又不加。這是因為在 C 與 C++,以及基於這兩種語言加以改良的 Objective-C ,星號(*)代表的意思是指向某個記憶體的指標(pointer), 而不加星號(*)表示是取值,例如:
int number = 0;//取值 char[] name = ['n','a','m','e']; char *str = name;//取址
而到了 Objective-C 時,除了 c 時代的語法以外,比較簡單區分的點就是:NSObject 的物件生成(new,alloc,copy)所建立出來的物件,通常都是用 *變數 這種方式宣告。如果是由靜態方式生成的,就不需要加上星號(*)。例如:
NSString *str = [[NSString alloc] initWithString : @"ABC"]; NSInteger number = [str intValue];
認識了取值與取址的宣告方式之後,就要進入主題(記憶體回收機制)了,記憶體要不要被系統回收完全是看該物件的保留計數器(retainCount)的數值,當該計數器達到0之後,物件所佔用的記憶體空間就會被收回,如果這時再對物件做操作的話,就會發生 EXE_BAD_ACCESS,的錯誤,也就是記憶體不當存取,程式會 crash 掉。
首先先認識一下針對物件的 retainCount 的操作有哪些指令:
- release:釋放,對 retainCount - 1。
- retain:保留,對 retainCount + 1。
- autorelease:自動釋放,由系統在一定週期對retainCount - 1。
一般物件被生成之後,retainCount 的值預設會是 1,下面的例子是生成一個物件,並且對物件做計數器的操作:
NSString *str = [[NSString alloc] initWithString : @"ABC"];//這時 retainCount = 1 [str retain];//這時 retainCount = 2 [str autolease];//這時 retainCount = 2 [str release];//這時 retainCount = 1 // 一段時間後,retainCount = 0,記憶體回收或許你會問,為什麼下完 autorelease 之後,retainCount 還是等於 2呢?這是因為剛剛說過的:autorelease:自動釋放,由系統在一定週期對retainCount - 1。所以要注意到 autorelease 不會馬上被減 1 !
那既然不會馬上被回收,autorelease 使用的時機與地點為何?
我的建議是:適用於拋棄式的區域變數,以及function的return value,例如:
- (NSString *) getResult { NSString *str = [[[NSString alloc] initWithString : @"ABC"] autorelease]; NSString *result = [[NSString alloc] initWithFormat : @"NAME : %@",str]; return [result autorelease]; }
我們都知道記憶體用完要隨時釋放,避免造成 leak ,所以上面的例子中可以看到對 str 物件下了 autorelease 的指令,所以 str 的 retainCount 變化如下:
initWithString(retainCount = 1) => autorelease(retainCount = 1) => 一段時間後(retainCount = 0)=> 回收。
同理可知,result 也要做相同的處理,於是就在 return 時,同時下了 autorelease 的指令,讓系統自動釋放並回收。
延伸閱讀:Objective-C 的記憶體回收機制(中)
延伸閱讀:Objective-C 的記憶體回收機制(下)
沒有留言:
張貼留言