開始使用ARC

Automatic Reference Counting (ARC)是編譯器自動管理Objective-C對象的一個功能,相對於不得不考慮retain和release操做來講,ARC讓咱們有更多的精力集中在咱們應用內有趣的代碼、object graphs和對象之間的關係上。安全


概要

ARC是用過來在編譯的時候添加適當的代碼來保證對象在有用的時候有效,沒有了就再也不有效了。從概念上講,ARC經過調用合適的內存管理方法遵循着和 manual reference counting(MRC)一樣的內存管理規則。app

爲了讓編譯器產生正確的代碼,ARC嚴格規定了你能夠調用的方法和怎樣使用toll-free bridging。ARC引入了新的用於對象引用和屬性聲明的生命週期標識符。函數

ARC支持Xcode 4.2 for OS X v10.6 and v10.7 (64-bit applications) and for iOS 4 and iOS 5。弱引用不支持OS X v10.6 and iOS 4。工具

Xcode提供了一個工具用來把非ARC代碼自動轉換到ARC代碼(例如移除retain和release的調用),解決的不能自動遷移的問題。這個工具會把工程中的全部文件都轉換成ARC,在使用非ARC方便的狀況下,你也能夠選擇某些文件使用ARC。oop

ARC概況

不用再記住何時該使用retain、release和autorelease了,ARC知道你的對象的使用週期而後在編譯的時候加入了合適的內存管理代碼。編譯器也會爲你生成合適的dealloc方法。若是你剛剛使用ARC,那麼傳統的Cocoa的命名規範在你管理MRC的代碼的時候是很是重要的。性能

一個完整的正確的Person類的實現是這樣的:spa

 

@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@property NSNumber *yearOfBirth;
@property Person *spouse;
@end
 
@implementation Person
@end

對象的屬性默認是strong的。指針

 

使用ARC,你可能實現這樣一個方法contrived code

 

- (void)contrived {
    Person *aPerson = [[Person alloc] init];
    [aPerson setFirstName:@"William"];
    [aPerson setLastName:@"Dudney"];
    [aPerson setYearOfBirth:[[NSNumber alloc] initWithInteger:2011]];
    NSLog(@"aPerson: %@", aPerson);
}

ARC接管裏內存管理,因此Person和NSNumber都不會形成內存泄露。orm

 

你也能夠安全的實現一個Person的方法takeLastNameFrom:

 

- (void)takeLastNameFrom:(Person *)person {
    NSString *oldLastname = [self lastName];
    [self setLastName:[person lastName]];
    NSLog(@"Lastname changed from %@ to %@", oldLastname, [self lastName]);
}

ARC可以確保 oldLastName  在NSLog以前不被釋放。

 

ARC強制的新規則

ARC提出了一些新的別的編譯器沒有的新規則。這些規定是爲了可以提供一個完整的可靠的內存管理模型。在一些狀況可以很簡單的提高性能,在一些狀況下可以簡化代碼或者讓你不用處理內存管理。若是你違背這些規則,在編譯的時候就會出錯,而不是在運行的時候產生錯誤。

 

  • 你不能顯示的調用dealloc,實現或者調用retain、release、retianCount或者autorealease。
             禁止使用   @selector(retain) @selector(release)等等
             若是你想要管理資源而不是釋放實例變量,你能夠實現一個dealloc方法。你不必也不能釋放實例變量,可是你能夠調用系統類或者非ARC代碼的                                                  [systemClassInstance setDelegate:nil]方法。
             在ARC下實現的dealloc方法不能調用[super dealloc]方法,會致使編譯錯誤。對於父類的調用是由編譯器自動和強制實現的。
             你仍然能夠調用CFRetian、CFRelease和一些與 Core Foundation-style對象有關的方法。
  • 你不能使用NSAllocateObject 和 NSDeallocateObject
             你用alloc來建立對象,Runtime系統來負責釋放對象。
  • 在C結構體中不能使用對象指針。
             代替結構體,你可使用一個Objective-C的類來管理數據
  • 不能隨便的轉換id和void*
             你必須使用特殊的轉換來告訴編譯器對象的聲明週期,你必須在Objective-C對象和做爲函數參數的Core Foundation類型之間這麼轉換。看下面的Toll-Free-Bridging。
  • 你不能是用NSAutoreleasePool
              ARC提供了@autoreleasepool塊來代替,比NSAutoreleasePool有更高的效率。
  • 你不能是用內存zones
              沒有必要再使用NSZone,它已經被Objective-C運行時系統忽略了。
        爲了可以和手動管理內存的代碼交互,ARC提出了嚴格的命名規範。
  • 不能提供以new開頭的獲取方法,這意味着你不能添加一個以new開頭的屬性,而不本身添加get方法。
    // Won't work:
    @property NSString *newTitle;
 
    // Works:
    @property (getter=theNewTitle) NSString *newTitle;

ARC提出了新的生命週期標識符

ARC提出了幾個生命週期標識符和弱引用。弱引用不會延長它指向的對象的生命週期。當沒有強引用指向這個對象的時候弱引用就會被賦值爲nil。
你應該在你的應用內充分利用這些標識符來管理object graph。ARC不能防止循環引用的發生,可是咱們能夠明智的使用弱引用來避免循環引用發生。

Property屬性

weak和strong能夠用做Preperty的屬性。就像下面這樣:
// The following declaration is a synonym for: @property(retain) MyClass *myObject;
@property(strong) MyClass *myObject;
 
// The following declaration is similar to "@property(assign) MyClass *myObject;"
// except that if the MyClass instance is deallocated,
// the property value is set to nil instead of remaining as a dangling pointer.
@property(weak) MyClass *myObject;
在ARC下,strong是默認的對象類型。

變量標識符

你能夠像使用const同樣使用下面的變量標識符:
__strong
__weak
__unsafe_unretained
__autoreleasing
  • __strong是默認的,一個對象只要有__strong類型指針指向它,它就有效。
  • __weak聲明瞭一個引用並不持有該對象。當沒有強引用指向弱引用指向的對象的時候,弱引用被賦值爲nil。
  • __unsafe__unretain聲明瞭一個不持有對象的引用。當沒有強引用指向該對象的時候,該引用不會被賦值爲nil,因此當一個該引用指向的對象被回收的時候,該引用是危險的。
  • __autorelease是用來聲明經過id*傳遞並返回autorelease的參數的。
你應該正確的使用標識符,當聲明一個對象的變量的時候正確的作法以下:
ClassName * qualifier variableName;
例如:
MyClass * __weak myWeakReference;
MyClass * __unsafe_unretained myUnsafeReference;
其餘變種寫法在技術上是不正確的,當時是被編譯器容許,想要了解更過能夠看這裏: http://cdecl.org/
考慮一下何時在棧變量上使用__weak屬性,考慮下面的狀況:
NSString * __weak string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]];
NSLog(@"string: %@", string);
雖然string是在初始化賦值以後使用的,可是在賦值的時候沒有強引用指向string,所以它馬上就會被釋放。NSLog語句會輸出一個null值(在此例子中編譯器會給出警告)。
你一樣要注意經過引用傳遞的對象,下面的代碼是沒有問題的:
NSError *error;
BOOL OK = [myObject performOperationWithError:&error];
if (!OK) {
    // Report the error.
    // ...
然而這個聲明是錯誤的並且是隱式的。
NSError * __strong e;
其實這個方法的聲明以下:
-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;
編譯器重寫的代碼以下:
NSError * __strong error;
NSError * __autoreleasing tmp = error;
BOOL OK = [myObject performOperationWithError:&tmp];
error = tmp;
if (!OK) {
    // Report the error.
    // ...
局部變量類型(__strong)和函數參數類型(__autorelease)不匹配,就會引發編譯器生成一個臨時變量。當你使用__strong變量地址的時候你能夠經過聲明參數爲__strong來得到原始指針。另外你也能夠聲明爲__autorelease。

使用生命週期標識符避免循環引用

你可使用生命週期標識符來避免循環引用,例如,典型的你有這麼一個父子對象關係,父親須要引用他的孩子,反之亦然,而後你建立一個父親到孩子的強引用關係和孩子到父親的弱引用關係。其餘的狀況可能更微妙,特別是涉及到block objects的時候。
在MRC手動引用計數模型下,__block id x;對x不會有retaining影響。可是在ARC下,__block id x;默認的retaining x就像其餘變量同樣。爲了在ARC下得到和MRC同樣的效果,你可使用__unsafe__unretained __block id x;就像__unsafe__unretained名字代表的同樣,擁有一個non-retianed的變量是危險的,所以這個是不提倡的。兩個比較好的解決方案是使用__weak(若是你不須要支持iOS4或者OS X v10.6),或者設置__block變量爲nil來打破循環引用。
下面的代碼片斷說明了一個在MRC下有時會用到的模式:
MyViewController *myController = [[MyViewController alloc] init…];
// ...
myController.completionHandler =  ^(NSInteger result) {
   [myController dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:myController animated:YES completion:^{
   [myController release];
}];
正像是描述的同樣,你可使用__block標識符,在completion handler內設置myController爲nil:
MyViewController * __block myController = [[MyViewController alloc] init…];
// ...
myController.completionHandler =  ^(NSInteger result) {
    [myController dismissViewControllerAnimated:YES completion:nil];
    myController = nil;
};
另外,你可使用一個臨時的__weak變量。下面的例子聲明瞭一個簡單是實現:
MyViewController *myController = [[MyViewController alloc] init…];
// ...
MyViewController * __weak weakMyViewController = myController;
myController.completionHandler =  ^(NSInteger result) {
    [weakMyViewController dismissViewControllerAnimated:YES completion:nil];
};
沒有了循環引用,可是你應該這樣用:
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...
    }
};
在一些狀況下若是__weak是不兼容的你可能使用__unsafe__unretained。然而對於循環引用這個可能變得不切實際,由於是很是可貴或者不可能驗證__unsafe__unretained指針是有效的和指向了一樣有問題的對象。

ARC使用了新的語句來管理Autorelease Pools

使用ARC,你不能再使用NSAutoreleasePool類來管理內存池了。代替的是使用@autoreleasepool塊:
@autoreleasepool {
     // Code, such as a loop that creates a large number of temporary objects.
}
這個簡單的結構能夠容許編譯器來推斷引用計數狀態。在入口處,一個autorelease pool被放進棧。在退出的地方(break,return,goto,fall-through等等)autorelease pool被彈出棧。爲了兼容已經存在的代碼,若是因爲異常退出,autorelease pool是不會被彈出棧的。
這個語法是在全部的Objective-C模型中均可以用。這個是比NSAutoreleasePool更加有效率的。所以你應該在使用NSAutoreleasePool的地方使用它。

Patterns for Managing Outlets Become Consistent Across Platforms(管理Outlets)

在ARC下,iOS和OS X 中outlets的聲明改變了,變成了跨平臺一致的了。你應該使用的典型的模型是:outlets應該是weak的,除了來自nib文件top-level對象的File`s Owner,應該是strong。

棧變量初始化爲nil

使用ARC,strong,weak和autoreleasing 棧變量如今會被隱式的初始化爲nil。例如:
- (void)myMethod {
    NSString *name;
    NSLog(@"name: %@", name);
}
NSLog語句將會輸出null而不是crashing。

使用編譯標誌來開啓和關閉ARC

你可使用-fobjc-arc編譯標誌來開啓ARC。若是一些文件使用MRC更方便的話,你能夠選擇按文件來使用ARC。對於默認開啓ARC的項目來講,你也可使用-fno-objc-arc來指定某些文件不適用ARC。
ARC支持Xcode4.2和OS X v10.6以後的64位應用,或者iOS4以後的。弱引用不支持OS X v10.6和iOS4,因此在Xcode4.1以前都不支持ARC。

管理Toll-Free-Bridging

待續。。。
相關文章
相關標籤/搜索