ARC 時代的內存管理

自動引用計數(ARC)是一種編譯器功能,它提供 Objective-C 對象的自動內存管理。ARC 不是必須考慮 retain 和 release 操做,而是讓你專一於有趣的代碼,對象圖以及應用程序中對象之間的關係。算法

摘要

ARC 的工做原理是在編譯時添加代碼,以確保對象在必要時存活,但不會一直存活。從概念上講,它經過爲你添加適當的內存管理調用,遵循與手動引用計數相同的內存管理約定。數組

爲了讓編譯器生成正確的代碼,ARC 限制了你可使用的方法以及如何使用免費橋接(toll-free bridging)。ARC 還爲對象引用和聲明的屬性引入了新的生命週期限定符。安全

ARC 在 Xcode 4.2 編譯的 OS X v10.6 和 v10.7 (64位應用程序) 以及 iOS 4 和 iOS 5 應用程序中提供支持。OS X v10.6 和 iOS 4 不支持弱引用。框架

Xcode 提供了一個工具,能夠自動執行 ARC 轉換的機械部分(例如刪除 retain 和 release 調用),並幫助你修復遷移器沒法自動處理的問題(選擇 Edit > Refactor > Convert to Objective-C ARC)。遷移工具將項目中的全部文件轉換爲使用 ARC。若是對某些文件使用手動引用計數更方便,也能夠選擇在每一個文件的基礎上使用 ARC。ide

ARC 概述

ARC 不會記住什麼時候使用 retain,release 和 autorelease,而是評估對象的生命週期要求,並在編譯時自動爲你插入適當的內存管理調用。編譯器還會爲你生成適當的 dealloc 方法。一般,若是你只使用 ARC,則只有在須要與使用手動引用計數的代碼進行互操做時,傳統的 Cocoa 命名約定纔是重要的。函數

Person 類的完整且正確的實現可能以下所示:工具

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

(默認狀況下,對象屬性是 strong。)oop

使用 ARC,你能夠實現這樣一種人爲的方法:性能

- (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 對象都不會泄露。優化

你還能夠安全地實現 Person 的 takeLastNameFrom: 方法,以下所示:

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

ARC 確保在 NSLog 語句以前不釋放 oldLastName。

ARC 實施新規則

爲了工做,ARC 強制使用其餘編譯器模式時不存在的一些新規則。規則旨在提供徹底可靠的內存管理模型; 在某些狀況下,他們只是執行最佳實踐,在其餘一些狀況下,他們簡化了代碼,或者是你沒必要處理內存管理的明顯推論。若是違反這些規則,則會當即獲得編譯時錯誤,而不是在運行時可能會出現的細微錯誤。

  • 你沒法顯式調用 dealloc,也沒法實現或調用 retain,release,retainCount 或 autorelease。

    禁止擴展到使用 @selector(retain),@selector(release) 等。

    若是須要管理除釋放實例變量以外的資源,則能夠實現 dealloc 方法。你沒必要(實際上你不能)釋放實例變量,但你可能須要在系統類和未使用 ARC 編譯的其餘代碼上調用 [systemClassInstance setDelegate:nil]。

    ARC 中的自定義 dealloc 方法不須要調用 [super dealloc](它實際上會致使編譯器錯誤)。連接到 super 是由編譯器自動執行的。

    你仍然能夠將 CFRetain,CFRelease 和其餘相關功能與 Core Foundation 樣式的對象一塊兒使用。

  • 你不能使用 NSAllocateObject 或 NSDeallocateObject。

    你可使用 alloc 建立對象; 運行時負責 deallocating 對象。

  • 你不能在 C 結構中使用對象指針。

    你能夠建立一個 Objective-C 類來管理數據,而不是使用結構。

  • id 和 void * 之間不是簡單的映射。

    你必須使用特殊的強制轉換來告訴編譯器有關對象的生命週期。你須要在 Objective-C 對象和做爲函數參數傳遞的 Core Foundation 類型之間進行轉換。

  • 你不能使用 NSAutoreleasePool 對象。

    ARC 提供了 @autoreleasepool 塊。它們具備比 NSAutoreleasePool 更有效的優勢。

  • 你不能使用內存區域(memory zones)。

    再也不須要使用 NSZone - 不管如何它們都會被現代的 Objective-C 運行時忽略。

爲了容許與 manual retain-release 代碼進行互操做,ARC 對方法命名施加了約束:

  • 你不能爲訪問者提供以 new 開頭的名稱。這反過來意味着你不能,例如,聲明一個名稱以 new 開頭的屬性,除非你指定一個不一樣的 getter:
// Won't work:
@property NSString *newTitle;
 
// Works:
@property (getter=theNewTitle) NSString *newTitle;
複製代碼

ARC 推出新的生命週期限定符

ARC 爲對象和弱引用引入了幾個新的生命週期限定符。弱引用不會延長它指向的對象的生命週期,而且在沒有對該對象的強引用時自動變爲 nil。

你應該利用這些限定符來管理程序中的對象圖。特別是,ARC不能防範強引用循環(之前稱爲 retain cycles)。明智地使用弱飲用將有助於確保你不會建立循環引用。

屬性特性

關鍵字 weak 和 strong 將做爲新聲明的屬性特性引入,如如下示例所示。

// 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 是默認值。只要存在指向它的強指針,對象就會保持「活着」。
  • __weak 指定不使引用對象保持活動狀態的引用。當沒有對象的強引用時,弱引用設置爲 nil。
  • __unsafe_unretained 指定一個引用,該引用不會使引用的對象保持活動狀態,而且在沒有對該對象的強引用時不會設置爲 nil。若是它引用的對象被釋放,則指針懸空(dangling)。
  • __autoreleasing 用於表示經過引用(id *)傳遞的參數,並在返回時自動釋放。

你應該正確裝飾變量。在對象變量聲明中使用限定符時,正確的格式爲:

ClassName * qualifier variableName;
複製代碼

例如:

MyClass * __weak myWeakReference;
MyClass * __unsafe_unretained myUnsafeReference;
複製代碼

其餘變體在技術上是不正確的,但編譯器「原諒」了。

在棧上使用 __weak 變量時要當心。請考慮如下示例:

NSString * __weak string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]];
NSLog(@"string: %@", string);
複製代碼

儘管在初始賦值以後使用了字符串,但在賦值時沒有對字符串對象的其餘強引用; 所以,它當即被釋放。log 語句顯示 string 的值爲 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)和參數(__autoreleasing)之間的不匹配會致使編譯器建立臨時變量。當你獲取 __strong 變量的地址時,能夠經過聲明參數 id __strong * 來獲取原始指針。或者,你能夠將變量聲明爲 __autoreleasing。

使用生命週期限定符來避免強引用循環

你可使用生命週期限定符來避免強引用循環。例如,一般若是你有一個以父子層次結構排列的對象圖表,而且父對象須要引用他們的孩子,反之亦然,那麼你將使父對象指向子對象之間的引用爲強引用,而且父對象與子對象之間的引用爲弱引用。其餘狀況可能更微妙,特別是當它們涉及 block 對象時。

在手動引用計數模式下,__block id x; 具備不保留 x 的效果。在 ARC 模式下,__block id x; 默認爲保留 x(就像全部其餘值同樣)。要在 ARC 下得到手動引用計數模式行爲,可使用 __unsafe_unretained __block id x;。然而,正如名稱 __unsafe_unretained 所暗示的那樣,具備非保留變量是危險的(由於它能夠懸掛),所以不鼓勵。兩個更好的選擇是使用 __weak(若是你不須要支持 iOS 4 或 OS X v10.6),或者將 __block 值設置爲 nil 以打破引用循環。

如下代碼片斷使用有時在手動引用計數中使用的模式說明了此問題。

MyViewController *myController = [[MyViewController alloc] init…];
// ...
myController.completionHandler =  ^(NSInteger result) {
   [myController dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:myController animated:YES completion:^{
   [myController release];
}];
複製代碼

如上所述,你可使用 __block 限定符並在完成處理程序中將 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];
};
複製代碼

可是,對於 non-trivial cycles,你應該使用:

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。然而,這對於 nontrivial cycles 來講變得不切實際,由於很難或不可能驗證 __unsafe_unretained 指針仍然有效而且仍然指向相同的對象。

ARC 使用新語句來管理自動釋放池

使用 ARC,你沒法使用 NSAutoreleasePool 類直接管理自動釋放池。相反,你使用 @autoreleasepool 塊:

@autoreleasepool {
     // Code, such as a loop that creates a large number of temporary objects.
}
複製代碼

這種簡單的結構容許編譯器推斷引用計數狀態。在進入時,推送自動釋放池。在正常退出(break,return,goto,fall-through 等)時,將彈出自動釋放池。爲了與現有代碼兼容,若是 exit 是由異常引發的,則不會彈出自動釋放池。

全部 Objective-C 模式都提供此語法。它比使用 NSAutoreleasePool 類更有效; 所以,咱們鼓勵你採用它代替使用 NSAutoreleasePool。

管理 Outlets 的模式在平臺上變得一致

用於在 iOS 和 OS X 中聲明 outlets 的模式隨 ARC 而變化,而且在兩個平臺上變得一致。你應該採用的模式是:outlets 應該是弱的,除了從文件全部者到 nib 文件(或故事板場景)中應該很強的頂級對象的出口。

棧變量初始化爲 nil

使用 ARC,如今使用 nil 隱式初始化 strong,weak 和 autoreleasing 棧變量。例如:

- (void)myMethod {
    NSString *name;
    NSLog(@"name: %@", name);
}
複製代碼

將爲 name 的值記錄 null,而不是崩潰。

使用編譯器標誌啓用和禁用 ARC

使用新的 -fobjc-arc 編譯器標誌啓用 ARC。若是對某些文件使用手動引用計數更方便,也能夠選擇在每一個文件的基礎上使用 ARC。對於使用 ARC 做爲默認方法的項目,可使用該文件的新 -fno-objc-arc 編譯器標誌爲特定文件禁用 ARC。

Xcode 4.2 及更高版本的 OS X v10.6 及更高版本(64位應用程序)以及 iOS 4 及更高版本支持 ARC。OS X v10.6 和 iOS 4 不支持弱引用。Xcode 4.1 及更早版本中沒有 ARC 支持。

管理 Toll-Free Bridging

在許多 Cocoa 應用程序中,你須要使用 Core Foundation 樣式的對象,不管是來自 Core Foundation 框架自己(例如 CFArrayRef 仍是 CFMutableDictionaryRef),仍是來自採用 Core Foundation 約定的框架(如 Core Graphics)(你可使用相似 CGColorSpaceRef 和 CGGradientRef 的類型))。

編譯器不會自動管理 Core Foundation 對象的生命週期; 你必須根據 Core Foundation 內存管理規則調用 CFRetain 和 CFRelease(或相應的特定於類型的變體)。

若是在 Objective-C 和 Core Foundation 風格的對象之間進行轉換,則須要使用強制轉換(在 objc/runtime.h 中定義)或 Core Foundation 樣式宏(在 NSObject.h 中定義)來告訴編譯器對象的全部權語義:

  • __bridge 在 Objective-C 和 Core Foundation 之間傳輸指針,不轉讓全部權。

  • __bridge_retained 或 CFBridgingRetain 將一個 Objective-C 指針強制轉換爲 Core Foundation 指針,並將全部權轉移給你。

    你有責任調用 CFRelease 或相關函數來放棄對象的全部權。

  • __bridge_transfer 或 CFBridgingRelease 將非 Objective-C 指針移動到 Objective-C,並將全部權轉移到 ARC。

    ARC 負責放棄對象的全部權。

例如,若是你有這樣的代碼:

- (void)logFirstNameOfPerson:(ABRecordRef)person {
 
    NSString *name = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
    NSLog(@"Person's first name: %@", name);
    [name release];
}
複製代碼

你能夠用如下代替它:

- (void)logFirstNameOfPerson:(ABRecordRef)person {
 
    NSString *name = (NSString *)CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
    NSLog(@"Person's first name: %@", name);
}
複製代碼

編譯器處理從 Cocoa 方法返回的 CF 對象

編譯器理解返回 Core Foundation 類型的 Objective-C 方法遵循歷史 Cocoa 命名約定。例如,編譯器知道,在 iOS 中,UIColor 的 CGColor 方法返回的 CGColor 不歸屬。你仍必須使用適當的類型轉換,如此示例所示:

NSMutableArray *colors = [NSMutableArray arrayWithObject:(id)[[UIColor darkGrayColor] CGColor]];
[colors addObject:(id)[[UIColor lightGrayColor] CGColor]];
複製代碼

使用全部權關鍵字轉換函數參數

在函數調用中在 Objective-C 和 Core Foundation 對象之間進行轉換時,須要告訴編譯器有關傳遞對象的全部權語義。Core Foundation 對象的全部權規則是 Core Foundation 內存管理規則中指定的規則; Objective-C 對象的規則在 Advanced Memory Management Programming Guide 中指定。

在下面的代碼片斷中,傳遞給 CGGradientCreateWithColors 函數的數組須要適當的強制轉換。arrayWithObjects: 返回的對象的全部權不會傳遞給函數,所以強制轉換爲 __bridge。

NSArray *colors = <#An array of colors#>;
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);
複製代碼

代碼片斷在如下方法實現中的上下文中示出。另請注意 Core Foundation 內存管理功能的使用,這些功能由 Core Foundation 內存管理規則決定。

- (void)drawRect:(CGRect)rect {
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
    CGFloat locations[2] = {0.0, 1.0};
    NSMutableArray *colors = [NSMutableArray arrayWithObject:(id)[[UIColor darkGrayColor] CGColor]];
    [colors addObject:(id)[[UIColor lightGrayColor] CGColor]];
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);
    CGColorSpaceRelease(colorSpace);  // Release owned Core Foundation object.
    CGPoint startPoint = CGPointMake(0.0, 0.0);
    CGPoint endPoint = CGPointMake(CGRectGetMaxX(self.bounds), CGRectGetMaxY(self.bounds));
    CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint,
                                kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
    CGGradientRelease(gradient);  // Release owned Core Foundation object.
}
複製代碼

轉換項目時的常見問題

遷移現有項目時,你可能會遇到各類問題。如下是一些常見問題以及解決方案。

  • 你沒法調用 retain,release 或 autorelease。

    這是一個功能。你也寫不出來:

    while ([x retainCount]) { [x release]; }
    複製代碼
  • 你不能調用 dealloc。

    若是要在 init 方法中實現單例或替換對象,一般會調用 dealloc。對於單例,請使用共享實例模式。在 init 方法中,你沒必要再調用 dealloc,由於在覆蓋 self 時將釋放該對象。

  • 你不能使用 NSAutoreleasePool 對象。

    請改用新的 @autoreleasepool {} 結構。這會強制你的自動釋放池上的塊結構,而且比 NSAutoreleasePool 快約六倍。@autoreleasepool 甚至適用於非 ARC 代碼。由於 @autoreleasepool 比 NSAutoreleasePool 快得多,因此許多舊的「性能黑客」能夠簡單地用無條件的 @autoreleasepool 替換。

    遷移器處理 NSAutoreleasePool 的簡單用法,但它沒法處理複雜的條件狀況,或者在新的 @autoreleasepool 的主體內定義變量並在其後使用的狀況。

  • ARC 要求你在 init 方法中將 [super init] 的結果分配給 self。

    如下在 ARC init 方法中無效:

    [super init];
    複製代碼

    簡單的解決方法是將其更改成:

    self = [super init];
    複製代碼

    正確的解決方法是這樣作,並在繼續以前檢查結果爲 nil:

    self = [super init];
    if (self) {
       ...
    複製代碼
  • 你沒法實現自定義 retain 或 release 方法。

    實現自定義 retain 或 release 方法會打破弱指針。想要提供自定義實現有幾個常見緣由:

    • 性能。

      請不要再這樣作了; NSObject 的 retain 和 release 的實現如今要快得多。若是你仍然發現問題,請提交錯誤。

    • 實現自定義弱指針系統。

      請改用 __weak。

    • 實現單例類。

      請改用共享實例模式。或者,使用類而不是實例方法,這樣就沒必要分配對象了。

  • 「Assigned」實例變量變得 strong。

    在 ARC 以前,實例變量是非擁有引用 - 直接將對象分配給實例變量並不延長對象的生命週期。爲了使屬性變強,你一般會實現或合成調用適當內存管理方法的訪問器方法; 相反,你可能已經實現瞭如下示例中顯示的訪問器方法來維護弱屬性。

    @interface MyClass : Superclass {
        id thing; // Weak reference.
    }
    // ...
    @end
    
    @implementation MyClass
    - (id)thing {
        return thing;
    }
    - (void)setThing:(id)newThing {
        thing = newThing;
    }
    // ...
    @end
    複製代碼

    對於 ARC,實例變量默認是強引用 - 將對象直接分配給實例變量會延長對象的生命週期。遷移工具沒法肯定實例變量什麼時候弱。 要保持與之前相同的行爲,必須將實例變量標記爲弱,或使用聲明的屬性。

    @interface MyClass : Superclass {
        id __weak thing;
    }
    // ...
    @end
    
    @implementation MyClass
    - (id)thing {
        return thing;
    }
    - (void)setThing:(id)newThing {
        thing = newThing;
    }
    // ...
    @end
    複製代碼

    或:

    @interface MyClass : Superclass
    @property (weak) id thing;
    // ...
    @end
    
    @implementation MyClass
    @synthesize thing;
    // ...
    @end
    複製代碼
  • 你不能在 C 結構中使用 strong ids。

    例如,如下代碼將沒法編譯:

    struct X { id x; float y; };
    複製代碼

    這是由於 x 默認 strong 引用,編譯器沒法安全地合成使其正常工做所需的全部代碼。例如,若是經過一些最終執行自由的代碼將指針傳遞給其中一個結構,則必須在釋放結構以前釋放每一個 id。編譯器沒法可靠地執行此操做,所以在 ARC 模式下不容許徹底禁用結構中的 strong ids。有幾種可能的解決方案:

    1. 使用 Objective-C 對象而不是結構。

      不管如何,這被認爲是最佳實踐。

    2. 若是使用 Objective-C 對象是次優的(可能你須要這些結構的密集數組),那麼請考慮使用 void *。

      這須要使用下面描述的顯式強制轉換。

    3. 將對象引用標記爲 __unsafe_unretained。

      這種方法對於這樣的半常見模式可能頗有用:

    struct x { NSString *S;  int X; } StaticArray[] = {
      @"foo", 42,
      @"bar, 97,
    ...
    };
    複製代碼

    你將結構聲明爲:

    struct x { NSString * __unsafe_unretained S; int X; }
    複製代碼

    若是對象能夠從指針下面釋放出來,這多是有問題的而且是不安全的,可是對於已知永遠像常量字符串文字同樣的事物來講它很是有用。

  • 你不能直接在 id 和 void * 之間進行轉換(包括 Core Foundation 類型)。

    這在 Managing Toll-Free Bridging 中有更詳細的討論。

常常問的問題

我如何看待 ARC?retains/releases 的位置在哪裏?

嘗試再也不考慮 retain/release 調用的位置,而是考慮應用程序算法。考慮對象中的「強弱」指針,對象全部權以及可能的引用循環。

我還須要爲個人對象編寫 dealloc 方法嗎?

也許。

因爲 ARC 不會自動執行 malloc/free,管理 Core Foundation 對象,文件描述符等的生命週期,所以你仍然能夠經過編寫 dealloc 方法來釋放這些資源。

你沒必要(實際上不能)釋放實例變量,但你可能須要在系統類和其餘未使用 ARC 編譯的代碼上調用 [self setDelegate:nil]。

ARC 中的 dealloc 方法不須要 - 或容許調用[super dealloc]; 連接到父類由運行時處理和強制執行。

ARC 中仍然有引用循環嗎?

是。

ARC 自動 retain/release,並繼承引用循環的問題。幸運的是,遷移到 ARC 的代碼不多開始泄漏,由於屬性已經聲明屬性是否 retaining。

blocks 如何在 ARC 中工做?

在 ARC 模式下向塊傳遞塊時塊「正常工做」,例如在返回中。你沒必要再調用 Block Copy。

須要注意的一件事是 NSString * __block myString 保留在 ARC 模式下,而不是可能懸空的指針。要獲取先前的行爲,請使用 __block NSString * __unsafe_unretained myString 或(更好的是)使用 __block NSString * __weak myString。

我可使用 Snow Leopard 爲 ARC 開發 OS X 應用程序嗎?

不能夠。Xcode 4.2 的 Snow Leopard 版本在 OS X 上根本不支持 ARC,由於它不包括 10.7 SDK。Snow Leopard 的 Xcode 4.2 確實支持 ARC for iOS,而 Xcode 4.2 for Lion 支持 OS X 和 iOS。這意味着你須要 Lion 系統來構建在 Snow Leopard 上運行的 ARC 應用程序。

我能夠在 ARC 下建立一個 retained 指針的C數組嗎?

是的,你能夠,如此示例所示:

// Note calloc() to get zero-filled memory.
__strong SomeClass **dynamicArray = (__strong SomeClass **)calloc(entries, sizeof(SomeClass *));
for (int i = 0; i < entries; i++) {
     dynamicArray[i] = [[SomeClass alloc] init];
}
 
// When you're done, set each entry to nil to tell ARC to release the object.
for (int i = 0; i < entries; i++) {
     dynamicArray[i] = nil;
}
free(dynamicArray);
複製代碼

有許多方面須要注意:

  • 在某些狀況下,你須要編寫 __strong SomeClass **,由於默認值是 __autoreleasing SomeClass **。
  • 分配的內存必須爲零填充。
  • 在釋放數組以前,必須將每一個元素設置爲 nil(memset 或 bzero 將不起做用)。
  • 你應該避免使用 memcpy 或 realloc。

ARC 慢嗎?

這取決於你正在測量什麼,但一般是「不」。編譯器有效地消除了許多無關的 retain/release 調用,而且已經投入了大量精力來加速 Objective-C 運行時。特別是,當方法的調用者是 ARC 代碼時,常見的「返回 retain/autoreleased 對象」模式要快得多,而且實際上並不將對象放入自動釋放池中。

須要注意的一個問題是優化器不在常見的調試配置中運行,所以指望在-O0處看到比在-Os更多的 retain/release 流量。

ARC 是否在 ObjC++ 模式下工做?

是。你甚至能夠在類和容器中放置 strong/weak idsD。ARC 編譯器在複製構造函數和析構函數等中合成 retain/release 邏輯以使其工做。

哪些類不支持弱引用?

你當前沒法建立對如下類的實例的弱引用:

NSATSTypesetter,NSColorSpace,NSFont,NSMenuView,NSParagraphStyle,NSSimpleHorizontalTypesetter 和 NSTextView。

注意:此外,在 OS X v10.7 中,你沒法建立對 NSFontManager,NSFontPanel,NSImage,NSTableCellView,NSViewController,NSWindow 和 NSWindowController 實例的弱引用。此外,在 OS X v10.7中,AV Foundation 框架中的任何類都不支持弱引用。

對於聲明的屬性,你應該使用 assign 而不是 weak; 對於變量,你應該使用 __unsafe_unretained 而不是 __weak。

此外,你沒法在 ARC 下從 NSHashTable,NSMapTable 或 NSPointerArray 的實例建立弱引用。

在對 NSCell 或使用 NSCopyObject 的其餘類進行子類化時,我該怎麼辦?

沒什麼特別的。ARC 負責處理你必須明確添加額外 retains 的狀況。使用 ARC,全部複製方法都應該只複製實例變量。

我能夠選擇退出 ARC 以獲取特定文件嗎?

能夠。

遷移項目以使用 ARC 時,-fobjc-arc 編譯器標誌將設置爲全部 Objective-C 源文件的缺省值。你可使用該類的 -fno-objc-arc 編譯器標誌爲特定類禁用 ARC。在 Xcode 中,在目標 Build Phases 選項卡中,打開 Compile Sources 組以顯示源文件列表。雙擊要爲其設置標誌的文件,在彈出式面板中輸入 -fno-objc-arc,而後單擊「完成」。

是否在 Mac 上棄用了GC(垃圾收集)?

OS X Mountain Lion v10.8 中不推薦使用垃圾收集,而且將在 OS X 的將來版本中刪除垃圾收集。自動引用計數是推薦的替代技術。爲了幫助遷移現有應用程序,Xcode 4.3 及更高版本中的 ARC 遷移工具支持將垃圾收集的 OS X 應用程序遷移到 ARC。

注意:對於面向 Mac App Store 的應用,Apple 強烈建議你儘快使用 ARC 替換垃圾回收,由於 Mac App Store 指南禁止使用已棄用的技術。

相關文章
相關標籤/搜索