三年前,Xcode開發就一直推崇使用ARC內存管理機制, 再往前的歲月裏使用的是MRC,那時候retain和release隨處可見,顯然MRC已經成爲歷史,着眼當下:
這裏簡要羅列了 ARC 的一些知識點概要,
這是一個學習並存在容錯的文檔翻譯。
Transitioning to ARC Release Notes
1. 首先介紹一個概念:
現在的 OS X系統 和 iOS系統 都是沒有 垃圾回收 的(GC Garbage Collection)蘋果使用的內存管理的方法是: 應用計數 機制。從原有的MRC 過度到現在更加先進的ARC 機 制。
2. ARC 使用中感受到最大的好處(如果曾有MRC體驗 will more feel):
對於果農來說,再也不用費盡心思考慮對象的retain 和 release 操作了,解放出龐大的能量,使得更能夠專注在有趣的code環節
3. 概要一下ARC:
3.1 ARC和MRC都是對遵循着相同的內存管理法則,然而ARC更加優秀,它會在編譯的時候(compile time)自動添加一些code來確保對象的必要和生命力。對於其可靠性,(maybe question apple)
3.2 爲了確保編譯器自動生成正確代碼,ARC 對toll-free bridging(Coare Fundation <-> OC Object)方法做了限制約束;ARC 添加了一些新的對象引用的生命週期修飾符, 和一些屬性聲明。
3.3 ARC 起用於系統版本: Xcode4.2 , OS X v10.7 ,iOS 5 ,
3.4 Xcode 更改工程到ARC模式方法:(choose Edit > Convert to Objective-C ARC);另外對於當個文件,可以使用編譯標識來啓動或者取消ARC(Target > Build Phases > Compile Sources):棄用ARC 使用 -fobjc-arc ; 禁用ARC 使用 -fno-objc-arc標識對應文件即可。
4. ARC中一些硬性規則:
這些規則都是爲了讓內存管理模式更加可靠,這些規則只在ARC 編譯模式下發生作用,既然是硬性的,如果違背就會即刻生成編譯時報錯。
4.1 不能實現或調用:retain,release,retainCount或者autorelease。拓展爲不能用@selector(retain),@selector(relase)等。
也不能直接調用dealloc函數,dealloc 函數在ARC機制下不需要(也不能、會導致編譯錯誤)調用【super dealloc】,它其實是在編譯時候自動添加到代碼中。對於Core Foundation-syle objects,任然可以使用CFRetain,CFRelease 。
4.2 不能使用 NSAllocateObject 或者 NSDeallocateObject。 使用alloc 穿件對象,運行時會處理好對象的釋放。
4.3 不能在C的結構體重使用 對象指針,與其使用結構體,可以使用OC對象來管理數據。
4.4 id 和 void * 之間存在差異,id 其實是一個對象結構體指針, 在Objective-C對象和Core Foundation類型之間需要做一些特別定位處理
NSMutableArray *colors = [NSMutableArray arrayWithObject:(id)[[UIColor darkGrayColor] CGColor]];
[colors addObject:(id)[[UIColor lightGrayColor] CGColor]];
4.5 不能使用NSAutoreleasePool對象,ARC 提供了@autoreleasepool 塊替代,比前者優秀很多。
4.6 無需再使用 NSZone ,OC的運行時機制會忽略它們
4.7 ARC 引入了一些對方法名和屬性命名的約束。不能用 new 開頭的給一個屬性的訪問器命名,除非設置了一個特意指定的getter:
// Won't work:
@property NSString *newTitle;
// Works:
@property (getter=theNewTitle) NSString *newTitle;
5. ARC 引入的生命時修飾符(Lifetime Qualifiers)
5.1 屬性修飾(Property Attributes)
關鍵字 weak 和 strong。
下面的聲明方法同義於: @property(retain) MyClass *myObject;//MRC
@property(strong) MyClass *myObject;//ARC
下面的聲明方法相似於: @property(assign) MyClass *myObject;//MRC
@property(weak) MyClass *myObject
在ARC下, stong 是默認的對象屬性類型。
5.2 變量修飾(Variable Qualifiers)
__strong 默認修飾,只要有一個強引用存在,對象就始終是‘alive’的。
__weak 特別標註不用保留引用對象的alive,當改對象不再存在強引用的時候,弱引用會將至置爲nil。
__unsafe_unretained,不保留引用對象的alive,對象武強引用是也不置爲nil,如果對象釋放,指針將懸垂。
__autoreleasing,
使用方式(ClassName * qualifier variableName)如:
MyClass * __weak myWeakReference;
MyClass * __unsafe_unretained myUnsafeReference;
注意當 __weak 變量實在stack 上的時候:
NSString * __weak string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]];
NSLog(@"string: %@", string);
clang會給出警告:(Assigning retained object to weak variable;object will be released after assignment)
打印結果是:string: (null) ???不得解??
5.3 使用修飾符,避免強引用循環(Strong Reference Cycles)
Quote by Linus Torvalds:"Talk is cheap Show me the code":
As described, instead, you can use a __block qualifier and set the myController variable to nil in the completion handler:
MyViewController * __block myController = [[MyViewController alloc] init…];
// ...
myController.completionHandler = ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
myController = nil;
};
-----------------
Alternatively, you can use a temporary __weak variable. The following example illustrates a simple implementation:
MyViewController *myController = [[MyViewController alloc] init…];
// ...
MyViewController * __weak weakMyViewController = myController;
myController.completionHandler = ^(NSInteger result) {
[weakMyViewController dismissViewControllerAnimated:YES completion:nil];
};
--------------
For non-trivial cycles, however, you should use:
MyViewController *myController = [[MyViewController alloc] init…];
// ...
MyViewController * __weak weakMyController = myController;
myController.completionHandler = ^(NSInteger result) {
MyViewController *strongMyController = weakMyController;
if (strongMyController) {
// ...
[strongMyController dismissViewControllerAnimated:YES completion:nil];
// ...
}
else {
// Probably nothing...
}
};
6. ARC 使用心得語法去管理 AutoRelease Pools:
不能直接使用 類:NSAutoreleasePool,可以使用 @autoreleasepool 塊語法,它更加強大。 如下
@autoreleasepool {
// Code, such as a loop that creates a large number of temporary objects.
}
這個簡單的結構可以讓編譯器合理處理引用計數的狀態。進入時,自動釋放吃就被嵌入(pushed),當正常退出的時候(break,return,goto,fall-through,and so on )自動釋放 池就被彈出,不過如果是意外退出的情況,釋放池不會彈出(popped)。
7. outlets 應該使用weak, 但是nib文件中一些來至文件的持有者的高層級的對象需要使用strong (window,menu bars,custom controller objects, a storyboard scene)
8. Stack 變量將被初始化爲nil。
- (void)myMethod {
NSString *name;
NSLog(@"name: %@", name);
}
9. 管理 Toll-Free Bridging
編譯器不會自動管理Core Foundation 對象的內存。需要直接更具框架的內存管理規則 調用CFRetain 和 CFRelease。
如果需要在OC 和CF 之間做處理 利用宏定義(objc/runtime.h) 或者CF類型的宏(定義在NSObject.h)中。
__bridge CF 和 OC 之間指針轉換 武轉換持有關係。
__bridge_retained / CFBridgingRetain 將一個OC 指針 切換大奧CF指針,並且轉移持有關係,需要自己負責釋放持有對象關係(使用CFRelease)
__bridge_transfer / CFBridgingRelease 將非OC指針 切換爲OC型,並且將持有關係交給ARC。
10.dealloc 函數
因爲ARC 不會自動 malloc/free,不管理CF對象、文件描述符 等的生命週期,所以需要自己寫一個dealloc函數來釋放這些資源。
完全不用去對一個實例變量進行release,但是需要自己處理一些不遵循ARC的代碼,如可能需要插入[self setDelegate:nil]到系統類中。
在ARC中是不允許對dealloc函數調用[super dealloc]的,應爲在運行時會自動調用這塊代碼。
參考文檔:ARC Release Notes