0

Automatic Reference Counting

| 12/27/2012
我們這裡來考慮一下一個堆疊的程式碼:
@implementation Stack {
 NSMutableArray *_array;
}

- (id)init {
 if (self = [super init])
  _array = [[NSMutableArray array] retain];
 return self;
}

- (void) push :(id) x {
 [_array addObject: x];
}

- (id) pop {
 id x = [_array lastObject];
 [_array removeLastObject];
 return x;
}

- (void) dealloc {    [_array release];
    [super dealloc];
}
@end


雖然我們很直覺的寫了上述的程式碼,但是應用程式 Crash 了。因為程式碼有一個很大的問題。眼尖的人可能看出來了 pop 這個方法是錯的,為什麼?

根據蘋果官方的統計,記憶體漏洞在所有 Mac, iOS 應用發生 Crash 的原因中排行第一;同時也是被 App Store 否決掉的最主要原因。但,就跟 C 語言一樣,Objective-C 的記憶體的管理依然是相當複雜且耗時的工程,那,到底有沒有什麼比較好的解決方案呢?

91 年,Java 出現,同時也將 Garbage Collection (GC) 這項技術發揚光大。Java 不僅繼承了 C 語言的許多特性、完整的物件導向支援,更好的是,我們不用繼續在 malloc 跟 free 之間打轉。可惜,沒有多長的光景,GC 的缺點便一覽無遺。確實,只要程式語言支援 GC,程式設計師就不必再耗上無謂的時間在撰寫記憶體管理的程式碼,也省去了在錯綜複雜的宣告中尋找 bug 所需要花上的多餘人力與成本,但隨之而來的,卻是 GC 所佔用龐大的記憶體空間、系統必須提供 GC 運作所需要的 CPU Time,甚至執行中的應用程式都有被暫停下來,好讓 GC runtime 先運作的現象:這個問題對及時性高的系統是絕大的致命傷。就在程式設計師們期待能夠省去記憶體管理的工程,又可以有絕佳效能的時候,Automatic Reference Counting (ARC) 誕生了。

ARC 是一種在編譯期間時運作的技術。這項技術的工作便是在程式碼編譯的時候分析原始碼,並且在適當的位置填補、加入記憶體管理的程式後才進入編譯的階段。也就是:release, retain, autorelease 這些老朋友,即使你不去寫,他也會自己幫你加入。其實,你甚至沒有辦法再繼續使用這些過去與我們朝夕相伴的方法了,在啟用 ARC 之後,新的編譯器會直接對這些老兵們報錯。可憐他們只有離去一途。

所以說,撰寫 ARC 程式碼!就是:少寫一些 Objective-C 的程式碼!

以下是四點我們將舊有的程式改為 ARC 的方法:

1. 移除掉所有的 retain, release, autorelease
2. 修改 property,將 retain 改成 strong,將 assign 改成 weak
3. NSAutoreleasePool class 無法繼續使用,改用 @autoreleasepool 語法
4. 移除回傳 block 時所用的 copy autorelease

ARC 除了上述講的優點之外,他還有其他的優勢存在。
使用 ARC 所產生的程式碼,將會較原先的程式碼執行速度還要快速:

NSObject retain/release -- 2.6x faster
@autoreleasepool -- 6x faster
objc_msgSend -- 33% faster

你有什麼原因不用它呢?

下一篇:深入探討 ARC