當你想用 NSString 或其他 Foundation framework classes 來做更多程式設計工作時,你需要一個更有彈性的系統,也就是使用 Autorelease pools。
當開發 Mac Cocoa 應用程式時,autorelease pool 會自動地幫你設定好。
基於 "Programming in Objective-C," Copyright © 2004 by Sams Publishing一書中的範例,並經過允許而刊載。
main.m #import #import #import int main( int argc, const char *argv[] ) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *str1 = @"constant string";
NSString *str2 = [NSString stringWithString: @"string managed by the pool"];
NSString *str3 = [[NSString alloc] initWithString: @"self managed string"];
// print the strings
printf( "%s retain count: %x\n", [str1 cString], [str1 retainCount] );
printf( "%s retain count: %x\n", [str2 cString], [str2 retainCount] );
printf( "%s retain count: %x\n", [str3 cString], [str3 retainCount] );
// free memory
[str3 release];
// free pool
[pool release];
return 0;
}
output constant string retain count: ffffffff
string managed by the pool retain count: 1
self managed string retain count: 1
如果你執行這個程式,你會發現幾件事:第一件事,str1 的 retainCount 為 ffffffff。
另一件事,雖然我只有 release str3,整個程式卻還是處於完美的記憶體管理下,原因是第一個常數字串已經自動被加到 autorelease pool 裡了。還有一件事,字串是由 stringWithString 產生的。這個 method 會產生一個 NSString class 型別的字串,並自動加進 autorelease pool。
千萬記得,要有良好的記憶體管理,像 [NSString stringWithString: @"String"] 這種 method 使用了 autorelease pool,而 alloc method 如 [[NSString alloc] initWithString: @"String"] 則沒有使用 auto release pool。
在 Objective-C 有兩種管理記憶體的方法, 1) retain and release or 2) retain and release/autorelease。
對於每個 retain,一定要對應一個 release 「或」一個 autorelease。
下一個範例會展示我說的這點。
基於 "Programming in Objective-C," Copyright © 2004 by Sams Publishing一書中的範例,並經過允許而刊載。
Fraction.h ...
+(Fraction*) fractionWithNumerator: (int) n denominator: (int) d;
...
Fraction.m ...
+(Fraction*) fractionWithNumerator: (int) n denominator: (int) d {
Fraction *ret = [[Fraction alloc] initWithNumerator: n denominator: d];
[ret autorelease];
return ret;
}
...
main.m #import #import "Fraction.h"
#import int main( int argc, const char *argv[] ) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Fraction *frac1 = [Fraction fractionWithNumerator: 2 denominator: 5];
Fraction *frac2 = [Fraction fractionWithNumerator: 1 denominator: 3];
// print frac 1
printf( "Fraction 1: " );
[frac1 print];
printf( "\n" );
// print frac 2
printf( "Fraction 2: " );
[frac2 print];
printf( "\n" );
// this causes a segmentation fault
//[frac1 release];
// release the pool and all objects in it
[pool release];
return 0;
}
output Fraction 1: 2/5
Fraction 2: 1/3
在這個例子裡,此 method 是一個 class level method。在物件建立後,在它上面呼叫 了 autorelease。在 main method 裡面,我從未在此物件上呼叫 release。
這樣行得通的原因是:對任何 retain 而言,一定要呼叫一個 release 或 autorelease。物件的 retainCount 從 1 起跳 ,然後我在上面呼叫 1 次 autorelease,表示 1 - 1 = 0。當 autorelease pool 被釋放時,它會計算所有物件上的 autorelease 呼叫次數,並且呼叫相同次數的 [obj release]。
如同註解所說,不把那一行註解掉會造成分段錯誤(segment fault)。因為物件上已經呼叫過 autorelease,若再呼叫 release,在釋放 autorelease pool 時會試圖呼叫一個 nil 物件上的 dealloc,但這是不允許的。最後的算式會變為:1 (creation) - 1 (release) - 1 (autorelease) = -1
管理大量暫時物件時,autorelease pool 可以被動態地產生。你需要做的只是建立一個 pool,執行一堆會建立大量動態物件的程式碼,然後釋放這個 pool。你可能會感到好奇,這表示可能同時有超過一個 autorelease pool 存在。