2011年11月16日 星期三

iOS - Training (1) - Memory Management

在iOS 5之前的版本需要處理記憶體相關的動作,在iOS 5加入了ARC功能後,iOS會自行處理沒被參考到的物件,且記憶體相關的API會不能使用

NSObject -----
Hello* hello = [Hello new]; 等同於
Hello* hello = [[Hello alloc] init];

new, alloc為NSObject中的行為,而init為NSObject中的建構子,
所以在定義時建議使用[[Hello alloc] init],會比較清楚。

在產生一個新物件時,物件的retainCount會預設為1
retain行為將物件的retainCount加1
release行為將物件的retainCount減1
copy行為產生一個新物件,但內容與舊物件相同,但為不同的記憶體位置,retainCount為1 (Java裡的clone)
PS NSArray, NSDictionary等Immutable物件copy後的記憶體位置不變,故copy與retain都會將retainCount加1
-----
NSAutoreleasePool -----
當行為需回傳一個物件時
-(Hello*) genHello
{
    Hello* wholeNewHello = [[Hello alloc] init];
    return wholeNewHello;
}
會造成memory leak,需要使用release行為,但若在return之前加入release的話會造成錯誤,因為物件在回傳之前retainCount為0而被回收掉
為解決此問題,使用autorelease行為
-(Hello*) genHello
{
    Hello* wholeNewHello = [[Hello alloc] init];

    return [wholeNewHello autorelease];
}
使用autorelease行為可以先將物件丟入autorelease pool裡,待資料傳遞結束後再將此物件釋出
以下程式碼示範用法
int main(int argc, const char* argv[])
{
    NSAutoreleasePool* pool1 = [[NSAutoreleasePool alloc] init];
    NSAutoreleasePool* pool2 = [[NSAutoreleasePool alloc] init];
    NSMutableString* str = [NSMutableString string];
    [str retain];
    NSLog(@"retain count %d", [str retainCount]);
    [pool1 drain];
    NSLog(@"retain count %d", [str retainCount]);

    return 0;
}
呼叫[pool1 drain]後,OS會將autorelease stack裡的pool1裡頭的物件和其上方的pool2裡的物件作release行為,故結果如下
retain count 2
retain count 1
Pool Stack中的關係如下

由上圖所示,當pool1呼叫drain行為時,pool1及pool2裡的物件會被執行一次release行為,故結果retain count才會由一開始的2變成1

建構子或行為前的修飾字元
"+": 與Java的static相同,執行完後面的行為後隨時會被回收
"-": 與Java的public相同,執行完後面的行為後因為有實體的記憶體位置,故會放入release pool裡等待釋放
-----
Property -----
可少寫getter與setter方法。
其修飾詞包括了readonly、assign、nonatomic來管理記憶體。

Usage
在interface宣告時使用property,
@interface Translator:NSObject
{
    Hello* lang;
}
@property (retain) Hello* lang;
@end
而在implement時使用synthesize。
@implementation Translator
@synthesize lang;
@end
------

資料出處《Object-C與iOS開發入門》

沒有留言:

張貼留言