【譯】蘋果官方手冊:開始使用ARC

自動引用計數(ARC)是一項編譯器功能,可以給Objective-C提供自動內存管理的能力。ARC使得程序員能專注于應用程序的代碼、對象圖和對象間關系上,而不是考慮那些保持(retain)和釋放(release)操作。


手動及自動引用計數的區別
手動及自動引用計數的區別

概覽

ARC的工作原理是在編譯期間增加相關代碼,從而確保對象們只在它們所需的時段內存活,而不是永遠存在。從概念上來說,它和手動引用計數(參考高級內存管理編程手冊譯文))遵循相同的內存管理機制,只不過它會自動添加適當的內存管理調用代碼。
為了使得編譯器能添加正確的代碼,ARC限制了某些方法的調用,以及你使用對象橋接(toll-free bridging,參考對象橋接類別)的方式。同時ARC也為對象的引用及屬性聲明(屬性聲明可以方便地為該類成員聲明訪問方法,并且可以默認地實現它們)引入了新的存活時間修飾符。
OS X v10.6 或 OS X v10.7(64位應用程序)上的Xcode 4.2開始支持ARC,iOS 4 和 iOS 5或更高版本支持ARC。但OS X v10.6 和 iOS 4 上的ARC不支持弱引用。
Xcode提供了自動轉換為ARC的工具(如移除retainrelease的調用)并幫助你自動修復遷移無法解決的問題(使用Edit > Refactor > Convert to Objective-C ARC)的工具。這個轉換工具將會把工程內所有的文件轉化為使用ARC的模式。如果在某些文件中使用手動引用計數更加方便的話,你也可以選擇僅在部分文件中使用ARC。
參考:

ARC 概述

ARC會分析對象的生存時間并自動地在編譯期間插入調用適當的內存管理方法的代碼,從而取代了以往不得不記住何時要使用retainreleaseautorelease的那些日子。編譯器同樣也會產生適當的dealloc方法。總的來說,如果你只使用了ARC的話,那么傳統的Cocoa命名約定就只在你需要跟使用手動引用計數的代碼交互的時候才顯得重要。
一個完整并正確的Person類的實現看起來像這樣:

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

(默認情況下,對象的屬性為strongstrong的具體介紹參考下文ARC引入的新的存活時間修飾符。)
使用了ARC之后,可以這樣來實現一個contrived方法:

- (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引入了一些其他編譯模式不存在的新規則。這些規則的意圖是提供一個全面可信任的內存管理模型;有時候,它們直接地帶來了最好的實踐體驗,也有時候它們簡化了代碼,甚至在你絲毫沒有關注內存管理問題的時候幫你解決了問題。如果違反了這些規則,你將會得到一個即使編譯器錯誤,而不是可能在運行期間才會發生的小bug。

  • 不能調用dealloc 方法,實現或調用retainreleaseretainCountautorelease 方法。
    這條規則同時適用于使用@selector(retain)@selector(release) 等。
    如果需要管理資源而不是釋放實例變量的時候,可以實現一個dealloc方法。并不需要(實際上也不能)釋放實例變量,但你可能需要在系統類和其他沒有使用ARC編譯的代碼中調用[systemClassInstance setDelegate:nil]
    在ARC中自定義的dealloc方法不需要調用[super dealloc](這將導致編譯錯誤)。對超類的調用鏈會自動在編譯器內實現。
    仍可以在Core Foundation風格的對象里使用CFRetainCFRelease等相關函數(參見下文管理對象橋接)。
  • 不能使用NSAllocateObjectNSDeallocateObject
    使用alloc創建對象,運行期會自動管理對象的釋放。
  • 不能在C結構體內使用對象指針。
    可以創建Objective-C類來管理數據,而不是使用結構體。
  • idvoid *之間不存在轉換。
    必須明確地寫出這種轉換,從而告訴編譯器對象的存活時間。必須在傳入函數參數的時候明確地在Objective-C對象及Core Foundation類型之間做出類型轉換。參考下文管理對象橋接
  • 不能使用NSAutoreleasePool對象。
    ARC使用@autoreleasepool代碼塊作為替代。這樣做的優點是比使用NSAutoreleasePool更有效率。
  • 不能使用內存區域。
    再也不需要使用NSZone了——在現代Objective-C運行期間它們將會被忽略。

為了和手動保持-釋放的代碼交互,ARC對方法的命名提出了限制:

  • 不能給訪問方法起開頭為new的名字。參考如下代碼,除非給getter方法指定了其它的名字,否則不能聲明那個名字開頭為new的屬性:
// 不可行:
@property NSString *newTitle;
// 可行:
@property (getter=theNewTitle) NSString *newTitle;

ARC引入的新的存活時間修飾符

ARC為對象引入了一些新的存活時間修飾符以及弱引用功能。弱引用并不會擴展它指向的對象的存活時間,并且當該對象沒有被強引用的時候自動置為nil

建議使用這些修飾符來管理程序的對象圖。特別地,ARC不會防止強引用循環(之前叫做保持循環——參考實際內存管理譯文))。審慎地使用弱引用將會幫助確保沒有出現強引用循環。

屬性(Property)的特征詞

(此處的“修飾詞”原文為“attribute”,一般應該翻譯成“屬性”,但因為“Property”一般翻譯過來也是“屬性”,所以為避免混淆,將其翻譯為“特征詞”)

關鍵字weakstrong被作為新的屬性聲明特征詞被引入。示例如下:

// 下邊的聲明方式和 “@property(retain) MyClass *myObject;” 等價
@property(strong) MyClass *myObject;
 
// 下邊的聲明和“@property(assign) MyClass *myObject;”類似
// 不同之處是當MyClass實例被銷毀時,
// 這個屬性值將會被置為nil,而不是變成野指針
@property(weak) MyClass *myObject;

在使用ARC的項目里,strong是默認的對象類型。

變量修飾詞

有如下變量修飾詞:

__strong
__weak
__unsafe_unretained
__autoreleasing
  • __strong是默認值。只要有指向它的強指針,對象就保持“存活”。
  • __weak表明引用并不保持被引用對象“存活”。弱引用會在沒有強引用指向該對象時置為nil
  • __unsafe_unretained表明引用并不保持被引用對象“存活”,并且在沒有強引用指向該對象時不會將自己置為nil。當引用的對象被銷毀后,該指針就成了野指針。
  • __autoreleasing用來注明由(id *)引用傳入并在返回后被自動釋放的參數。

我們需要正確地修飾變量。當在聲明變量的時候使用修飾詞時,正確的格式如下:

類名 * 修飾詞 變量名;

例如:

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的值為空。(編譯器在這種情況下會發出警告。)
同時也要關注由引用傳遞的對象。下邊的代碼可以運行:

NSError *error;
BOOL OK = [myObject performOperationWithError:&error];
if (!OK) {
    // 報告這個錯誤。
    // ...

然而,異常的定義是隱含的:

NSError * __strong e; ```
并且方法的定義將通常是:

-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error; ```
于是編譯器會將代碼重寫為這個樣子:

NSError * __strong error;
NSError * __autoreleasing tmp = error;
BOOL OK = [myObject performOperationWithError:&tmp];
error = tmp;
if (!OK) {
    // 報告這個錯誤
    // ...

由于局部變量聲明(__strong)和參數聲明(__autoreleasing)之間的不匹配,導致編譯器會創建臨時變量。當需要獲取一個__strong變量的地址時,可以通過給參數聲明id __strong *來獲取到原本的指針。不然的話,就將變量聲明為__autoreleasing

使用存活時間修飾符來避免強引用循環

例如,你的程序具有這樣一種結構,對象與對象之間構成一種雙親-子女的層級關系,并且雙親需要依賴子女值得改變做出變化,這時候可以令雙親對子女為強關系,而子女對雙親為弱關系。其它的情況可能會更加微妙,特別是當它們調用了塊對象(塊對象是一種C級別的句法和運行時功能,它能用來組成那些可作為參數傳遞的、隨意存儲的并可以用在多線程中的函數表達式。)的時候。
在手動引用計數模式下,__block id x;不會保持x。但在ARC模式中,__block id x;默認會保持x(就像其他的值一樣)。為了在ARC模式中得到和手動引用計數一樣的行為,可以使用__unsafe_unretained __block id x;。然而,擁有一個沒有保持的變量是危險的(因為它可能會變成野指針),所以并不推薦這種方式。兩種更好的選擇是使用__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修飾符,并在completion處理語句內將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 {
        // 可能什么都沒有……
    }
};

在一些情況下如果這個類不能很好地配合__weak使用的話,可以嘗試用__unsafe_unretained。然而,這在間接循環中不太現實,因為驗證一個__unsafe_unretained指針是不是依然有效很難,甚至是不可能的。

ARC使用新的管理自動釋放池的聲明方式

使用ARC時,不能直接使用NSAutoreleasePool類來管理自動釋放池。而是應該使用@autorelease代碼塊:

@autoreleasepool {
    // 代碼,例如創建大量臨時對象的循環。
}

這種簡潔的結構使得編譯器可以推斷出引用計數的狀態。在進入代碼塊時,一個自動釋放池進棧。在正常退出(break,return,goto,fall-through等語句)時,自動釋放池被彈出。為了和已存在的代碼共存,如果因為異常造成的退出,自動釋放池不會被彈出。
這種句法在所有的Objective-C模式中都是可用的。這比使用NSAutoreleasePool更有效率;所以我們鼓勵使用這種方式替換NSAutoreleasePool

管理出口的語句在多個平臺實現統一

在iOS和OS X中用來聲明出口(outlet,出口本質是一個屬性,但它的值可以在nib文件中圖形化地設置)的語句因為ARC而發生了改變,并開始在兩個平臺統一起來。出口應該是weak的,但那些在nib文件(或故事板場景)的來自文件所有者的頂級對象,那些對象應當是strong的。
完整的詳細信息參考資源編程指南中的Nib 文件

棧變量被初始化為nil

使用ARC之后,強、弱和自動釋放的棧變量現在會默認初始化為nil。例如:

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

日志會輸出空的name值,而不是崩潰。

使用編譯器標識來啟用和禁用ARC

可以使用一個新標識-fobjc-arc來啟用ARC。如果在某些文件中使用手動引用計數更方便的話,也可以僅對部分文件使用ARC。對于默認即是ARC模式的工程,可以使用另一個新的編譯器標識-fno-objc-arc來禁用針對該文件的ARC。
OS X v10.6 或 OS X v10.7(64位應用程序)上的Xcode 4.2開始支持ARC,iOS 4 和 iOS 5或更高版本支持ARC。但OS X v10.6 和 iOS 4 上的ARC不支持弱引用。Xcode 4.1及更早版本不支持ARC。

管理對象橋接

在很多Cocoa應用程序中需要使用Core Foundation庫風格的對象。包括出自Core Foundation框架本身(例如CFArrayRefCFMutableDictionaryRef)的還有出自符合Core Foundation約定標準的其他框架(你可能會用到CGColorSpaceRefCGGradientRef這些類型)的對象。
編譯器會自動管理Core Foundation對象的存活時間,必須根據Core Foundation內存管理規則(參考Core Foundation的內存管理編程指南)調用CFRetainCFRelease
如果要在Objective-C及Core Foundation風格對象之間做類型轉換,需要使用類型轉換(在objc/runtime.h中定義)及Core Foundation風格宏(在NSObject.h中定義)告訴編譯器關于對象間所有權關系的語義:

  • __bridge可以將一個指針在Objective-C和Core Foundation之間相互轉換,但不會轉換所有權關系。
  • __bridge_retainedCFBridgingRetain將一個Objective-C指針轉換為一個Core Foundation指針,并同時轉換所有權關系。
  • __bridge_transferCFBridgingRelease將一個非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對象

編譯器可以理解Objective-C方法返回的遵循Cocoa命名約定(參考高級內存管理編程手冊譯文))的Core Foundation類型。例如,編譯器知道,在iOS中,由UIColorCGColor方法返回的CGColor對象,并沒有被擁有。仍然必須使用適當的類型轉換,就像下邊的例子:

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

使用所有權關鍵字轉換函數參數

當在函數調用時在Objective-C和Core Foundation對象之間轉換時,需要告訴編譯器有關傳入對象的所有權信息。Core Foundation對象的所有權規則在Core Foundation內存管理規則中給出(參考 Core Foundation的內存管理編程指南);Objective-C對象的規則在高級內存管理編程手冊譯文))中給出。
下面的代碼片段,傳入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);  // 釋放擁有的Core Foundation 對象
    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);  // 釋放擁有的Core Foundation 對象。
}

轉換工程時的常見問題

在遷移一個已經存在的項目時,很可能會遇到許多問題。這里列出常見問題的解決方案。

  • 不要調用retainreleaseautorelease
    這是一個特征。同時你也不能這樣寫:
while ([x retainCount]) { [x release]; }
  • 不要調用dealloc
    當在init方法中實現一個單例或替換一個對象時,通常會調用dealloc方法。對于單例來說,使用共享實例模式。在init方法里,永不需要調用dealloc,因為當覆蓋self的時候,對象會自動被釋放。
  • 不要使用NSAutoreleasePool對象。
    使用@autoreleasepool{}結構代替。規則強制使用代碼塊作為自動釋放池,而且這比使用NSAutoreleasePool快6倍。@autoreleasepool甚至還可以在非ARC代碼中使用。由于@autoreleasepoolNSAutoreleasePool速度快了太多太多,一些舊的“性能提升花招”統統可以被@autoreleasepool替換掉了。
    轉換工具可以處理簡單使用NSAutoreleasePool的情況,但它不能處理那些具有復雜條件或者變量在新的@autoreleasepool體內定義卻又在之外使用的情況。
  • ARC需要在init方法中將[super init]的結果分配給self
    下邊所示的init方法中的代碼在ARC下是不合法的:
[super init];

簡易的修復方式是:

self = [super init];

更加合理的修復方式在繼續之前再檢查結果是否為nil

self = [super init];
if (self) {
    ...
  • 不要實現自定義的retainrelease方法。
    實現自定義的retainrelease方法會破壞弱引用指針。以下是常見的幾個想要提供自定義實現的理由:
  • 運行效率。
    請不要再這么做了;NSObject中的retainrelease現在已經足夠快了。如果你仍然發現有問題,請提交bug。
  • 為了實現自定義的弱指針系統。
    請使用__weak作為代替。
  • 為了實現單例類。
    使用共享實例模式作為代替。或者,使用類方法代替實例方法,類方法可以避免創建對象。
  • “直接賦值”的實例變量變成強指針了。
    在ARC出現之前,實例變量們不存在所有權關系——直接將一個對象分配給一個實例變量不會擴展對象的存活時間。為了創建一個強關系,總是需要實現或合成包含了對應內存管理方法調用的訪問者方法;不同的是,有時為了維持一個弱關系,可能會這么實現一個訪問者方法:
@interface MyClass : Superclass {
    id thing; // 弱引用。
}
// ...
@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結構體中使用強id
    例如,下面的代碼無法被編譯:
struct X { id x; float y; };

這是由于x默認是強引用,編譯器無法保證令所有的相關的代碼正常地運行。例如,如果通過某些代碼給這些結構體傳入了一個指針,并在之后進行了free操作,所有id指向的對象都必須在結構體釋放之前被釋放掉。編譯器并不能可靠地做到這一點,所以強引用的id是不能再ARC模式下的結構體中存在的。
這里有一些可行的解決方案:
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; }

這可能會造成一些困難,并且如果對象在這之外被釋放的話這個指針就是不安全的,但它確實對諸如字符串常量之類的從一開始就確定永久存活的對象非常有用。

  • 不要直接在idvoid *(包括Core Foundation類型)之間進行類型轉換。
    管理對象橋接那一部分有詳細討論。

常見問答

我該怎么理解ARC?它在哪兒添加了ratain/release?

嘗試不要去琢磨retain/release在那兒被放置和調用這回事,而是多思考應用程序的邏輯與算法。多琢磨對象的“強和弱”、對象間的關系以及保持循環的避免。

我需要給對象寫dealloc方法嗎?

也許是吧。
因為ARC并不會自動malloc/free,所以對Core Foundation對象的生存時間管理,文件描述符等,這類資源仍需要通過編寫dealloc方法來釋放。
不應(事實上也不能)釋放實例變量,但你可能會給系統類和其他非ARC生成的代碼調用[self setDelegate:nil]。
ARC中的
dealloc方法不需要——或者說不允許——調用[super dealloc];**對超類的調用鏈會在運行時自動處理。

在ARC中仍然存在保持循環的問題嗎?

是的。
ARC自動地保持/釋放,所以也繼承了保持循環的問題。幸運的是,將代碼轉移至ARC后將很少發生內存泄露,因為屬性之間的關系已經明確了。

在ARC中代碼塊如何工作?

在ARC模式下,代碼塊“只在”你將其傳入棧時工作,例如在返回語句內。不再需要調用代碼塊的復制了。
要注意的一件事是,NSString * __block myString在ARC模式下被保持了,這不會造成野指針的問題。如果要執行之前的行為,使用__block NSString * __unsafe_unretained myString或(仍然是更好的選擇)__block NSString * __weak myString

我可不可以在Snow Leopard上開發基于ARC的OS X應用程序?

不可以。Snow Leopard版的Xcode 4.2完全不支持OS X上的ARC,因為它并沒有包含10.7 的SDK。但Snow Leopard版Xcode 4.2支持iOS上的ARC。Lion版的Xcode 4.2同時支持OS X和iOS。這意味著你需要Lion系統來構建運行在Snow Leopard上的ARC應用程序。

我可以創建一個包含被已保持了的指針的C數組嗎?

可以,舉例如下:

// 使用calloc()來獲取填滿了0的內存區域。
__strong SomeClass **dynamicArray = (__strong SomeClass **)calloc(entries, sizeof(SomeClass *));
for (int i = 0; i < entries; i++) {
     dynamicArray[i] = [[SomeClass alloc] init];
}
 
// 當完成工作后,將所有成員設為nil,從而告訴ARC釋放對象。
for (int i = 0; i < entries; i++) {
     dynamicArray[i] = nil;
}
free(dynamicArray);

一些需要記住的要點如下:

  • 在一些情況下你需要使用**__strong SomeClass ** ,因為默認情況下是__autoreleasing SomeClass ** **。
  • 申請的內存區域必須是被0填滿的。
  • 在釋放數組之前,必須將數組內每個元素都置為nilmomsetbzero將不起作用)。
  • 避免memcpyrealloc

ARC會拖慢運行速度嗎?

這取決于你的標準,但通常來說是“不”。編譯器可以高效地排除一些無用的retain/release調用,并且很多的經歷都被投入到提升Objective-C的運行速度上去了。特別地,當方法的調用者是ARC代碼時,常見的“返回一個保持/自動釋放對象”代碼段比沒有把對象放進自動釋放池的情況快很多。
需要注意的一個問題是優化程序不會在默認調試結構中使用,所以預計在-O0模式
下將會比-Os模式下看到更多的retain/release調用。

ARC可以運行在ObjC++模式下嗎?

當然可以。你甚至還可以在類或容器中放置強/弱的id對象。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中不能給NSHashTableNSMapTableNSPointerArray的實例創建弱引用。

當我繼承一個使用了NSCopyObject的類,如NSCell時,我需要做些什么?

沒有任何特殊的。ARC會關注之前需要你明確添加額外的ratain語句的地方。有了ARC,所有的復制方法只需要復制實例變量就可以了。

我能只指定一部分文件使用ARC嗎?

可以。
當你將一個工程遷移到ARC模式下時,-fobjc-arc編譯器標識默認會給所有Objective-C源文件設置上。你可以使用-fno-objc-arc編譯器標識給某個具體類禁用ARC功能。在Xcode中Build Phases選項卡里,打開Compile Sources組展開源文件列表。雙擊你想設置標識的文件,在彈出的面板里輸入-fno-objc-arc并點擊Done,完成設置。

Xcode設置界面
Xcode設置界面

在Mac上,GC(垃圾回收)功能過時了嗎?

垃圾回收功能自從OS X Mountain Lion v10.8版本就過時了,并將在OS X的未來版本中移除。推薦使用自動引用計數來代替這項技術。著力于遷移現有的應用程序,Xcode 4.3及更新版本中的ARC遷移工具支持將基于垃圾回收的OS X應用程序遷移至ARC。

要點:那些想要上架Mac APP Store的應用,蘋果強烈建議盡快將垃圾回收替換為ARC,因為Mac App Store的方針(參考Mac App Store審核方針)禁止使用過時的技術。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內容