ARC編程指南

什么是Automatic Reference Counting?

Automatic Reference Counting(ARC)是一個編譯器特性,它為Objective-C對象提供自動內存管理機制。相比手動地retain/release,ARC讓你專注于其它代碼,而不需要再考慮retain/release。下圖顯示了ARC和非ARC的區別。
[圖片上傳失敗...(image-f3472b-1574849633608)]

啟用 ARC之后,編譯器會自動在適當的地方插入適當的 retain, release,autorelease 語句。你不再需要擔心內存管理,因為編譯器為你處理了一切。

在使用toll-free bridging的時候,為了讓編譯器能插入正確的代碼,ARC做了一些限制。ARC還引進了新的生命周期修飾符。

ARC支持Xcode 4.2、OS X v10.6、iOS 4。但是weak指針在OS X v10.6和iOS4中不支持。

Xcode還集成了一個工具,可以把非ARC代碼轉化成ARC代碼(Edit > Refactor > Convert to Objective-C ARC)。這個工具可以把工程中所有的文件轉化成ARC代碼。你也可以手動指定某些文件使用ARC,而另一些文件不使用ARC。

更多知識:
Advanced Memory Management Programming Guide
Memory Management Programming Guide for Core Foundation

ARC 簡介

使用ARC,你不需要記住什么使用retainreleaseautorelease,ARC會自動處理對象的聲明周期,編譯的時候在合適的地方插入內存管理代碼。一般而言,只有當你需要和手動引用計數代碼交互的時候,傳統的Cocoa命名規則才是重要的。

一個完整正確的Person類的定義可能看起來像這樣:

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

@implementation Person
@end

(對象的屬性默認是strong,strong的含義會在下面講到)

使用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引進了新的規則。這些規則旨在為ARC提供可靠的內存管理模型。在一些情況下,它們只是采取最好的做法。在另外一些情況下,它簡化你的代碼,或者明顯地告訴你你不需要處理內存管理。如果你違反了這些規則,你會得到一個編譯錯誤,而不是運行時錯誤。

  • 你不能顯式地調用dealloc方法,也不能定義或者調用retainreleaseretainCountautorelease方法。
    禁止使用@selector(retain)@selector(release)等燈
  • 你可能需要定義dealloc方法來管理資源。你不用(實際上你不能)release實例變量,但是你可能需要在系統類或者沒有使用ARC編譯的代碼中調用[systemClassInstance setDelegate:nil]
    自定義dealloc方法中不需要調用[super dealloc](實際上會導致編譯錯誤)。父類方法的調用編譯器會自動幫你完成。
    你仍然可以對CoreFoundation對象使用CFRetainCFRelease或一些其它相關的方法。
  • 你不能使用NSAllocateObject或者NSDeallocateObject
    你用alloc創建對象;runtime處理對象的釋放。
  • 在C結構體中你不能只用對象指針。
    你可以創建一個Objective-C類來代替結構體來處理數據。
    id類型和void指針指間不能隨意轉換。
    你必須指定轉換修飾符來告訴編譯器對象的生命周期。你需要在函數傳的參數中處理
    Objective-CCoreFoundation*對象之間的轉換。
  • 你不能使用NSAutoreleasePool對象。
    ARC提供了@autoreleasepool快來代替它。它比NSAutoreleasePool更高效。
  • 你不能使用內存zones
    沒有必要使用NSZone了,它們會被modern Objective-C runtime忽略掉。

為了允許和手動retain-release代碼交互,ARC限制了方法命名:
你不能給屬性起一個以new開頭的名字。反過來說,除非你指定一個不同的getter方法,否則你不能定義一個new開頭的屬性

// Won't work:
@property NSString *newTitle;

// Works:
@property (getter=theNewTitle) NSString *newTitle;

方法也是一樣,如果你在Person類中寫了一個叫newPersonName的方法,編譯器會假設這個方法返回一個新分配的對象。如果你的代碼只在ARC下,或者非ARC下運行,不會有什么問題。但是如果你使用ARC和非ARC混編,就會出現問題。如果定義這個方法的類用非ARC編譯,但是調用代碼用ARC編譯,你的程序會崩潰;相反,如果定義這個方法的類用ARC編譯,調用代碼用非ARC編譯,就會發生內存泄漏。
雖然你不能改變編譯器的行為,但是可以用clangNS_RETURNS_NOT_RETAINEDNS_RETURNS_RETAINED來改變方法的行為。你的newPersonName可以像下面那樣申明,來讓它返回一個不被retain的對象。
- (NSString ) newPersonName NS_RETURNS_NOT_RETAINED;
但是不推薦這種方法,你可以為方法起一個不同的名字,比如- (NSString *) personName;。但是,如果方法定義在靜態庫里,你沒法修改源碼,你可以用這兩個
clang*宏。

ARC引進了新的生命周期限定符

ARC為對象和肉引用引進了一些新的生命周期限定符。weak指針不會擁有對象,并且當沒有其它strong指針指向這個對象的時候,weak指針會自動置為nil
你應該充分利用這些限定符來管理你的程序中的對象。特別提到一點,ARC不會
警告strong reference cycles(通常被叫做retain cycles)。恰當地使用weak關系可以幫你避免cycles。

Property屬性

屬性新增加了weak和strong關鍵字,如下面的例子所示。

 // 下面的定義和這個是同義詞: @property(retain) MyClass *myObject;
@property(strong) MyClass *myObject;



// 下面的定義和"@property(assign) MyClass *myObject;"類似
// 但是有一點不同,如果myObject指向的對象被釋放了,屬性會自動置為nil,而不是變成野指針。
@property(weak) MyClass *myObject;

在ARC下,默認的對象類型是strong。

變量限定符

你使用下面的生命周期限定符來修飾變量。

  • __strong
  • __weak
  • __unsafe_unretained
  • __autoreleasing

__strong是默認的修飾符. 只要有一個strong指針指向對象,對象就保持存活。
__weak指定個了一個不擁有對象的引用。一個weak引用在沒有其它strong引用指向對象的時候會自動置為nil。
__unsafe_unretained指定了一個不擁有對象的引用,當沒有其它strong引用指向對象的時候,它不會被自動置為nil。當引用的對象釋放之后,這個指針就會變成野指針。
__autorelease用來表示參數以引用傳遞,并返回autoreleased對象。
你應該正確地修飾變量。當對一個變量使用修飾符的時候,正確的格式是:

ClassName * qualifier variableName;

舉個例子:

MyClass * __weak myWeakReference;
MyClass * __unsafe_unretained myUnsafeReference;

一些其它的變體在技術上講是錯誤的,但是可以被編譯器允許。想了解的話,查看
在棧中使用__weak變量的時候要注意。考慮下面的例子:

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

盡管string在初始化賦值之后就用到了,但是在賦值執行的時候,沒有其它strong引用指向這個string;它馬上被釋放了。log語句顯式string的值是null。(編譯器在這種情況下會有一個警告)。
你還需要考慮對象以引用傳遞的情況。下面的代碼會工作:

NSError *error;
BOOL OK = [myObject performOperationWithError:&error];
if (!OK) {
    // Report the error.
    // ...

然而,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

使用生命周期限定符來避免Strong Reference Cycles

你可以使用生命周期限定符來避免strong reference cycles。例如,兩個對象互相 retain 時,會導致兩個對象都無法被釋放,這也是內存泄漏的常見原因之一。然后你可以把其中一個指針定義成weak,來打破循環。其它情況可能更微妙一些,特別是調用block對象的時候。


在手動引用計數模式下,__block id x;可以不retainingx。在ARC模式下,__block id x;默認會retainingx(就像其它變量一樣)。為了在ARC中獲得手動引用計數一樣的效果,你可以使用__unsafe_unretained __block id x;。像它的名字一樣,有一個non-retained對象是很危險的(因為它可能變成野指針),因此不鼓勵這么做。兩種不同的選擇是使用__weak(如果你不需要支持iOS 4或OS X v10.6),或者把這個__block值設置成nil來打斷retain cycle.
下面的代碼片段使用手動引用計數有時使用的一個模式來說明這個問題。

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

像上面講的那樣,你可以用一個__block限定符,然后在completion handlermyController變量設置成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];
};

前面講到weak指針,沒有其它strong指針指向對象的時候會自動置為nil,為了保證block執行期間對象不被釋放(可能在其它線程中被釋放),你應該這么做:

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.
}

這個簡單的結構允許編譯器思考引用計數狀態。進入塊的時候,一個自動釋放池被push進棧。在退出的時候(breakreturngotofall-through等等)自動釋放池被pop。為了兼容現有的代碼,如果因為一個異常退出,自動釋放池不會被pop
這個語法在所有Objective-C模式下都支持。它比NSAutoreleasePool類效率更高;鼓勵用它來代替NSAutoreleasePool的位置。

平臺間定義Outlets的模式變得一致

在iOS和OS X中使用ARC定義outlets的模式變得一致了。你應該遵循的模式是:outlets應該是weak,除了那些nib文件(或者storyboardscene)中頂層的對象應該是strong。
詳細的信息可以查看Resource Programming Guide中的Nib Files

棧變量初始值為nil

使用ARC時,strongweakautoreleasing棧變量隱式地用nil初始化。例如:

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

不會崩潰,而是打印name的值為null

使用編譯Flags來啟用或關閉ARC

你用-fobjc-arc編譯標志來開啟ARC。如果大部分使用手動引用計數更方便的話,你可以選擇以文件為單位開啟ARC。對于那些開啟了ARC的工程,你可以用-fno-objc-arc編譯標志來給指定的文件關閉ARC
ARC支持Xcode4.2以上,OS X v10.6以上,iOS4.0以上。Weak引用不支持OS X v10.6和iOS 4。Xcode 4.1和之前的版本不支持ARC

處理Toll-Free Bridging

在許多Cocoa應用中,你需要用到Core Foundation風格的對象。不管你是直接只用Core Foundation框架(比如CFArrayRefCFMutableDictionaryRef),還是使用集成了Core Foundation框架的一些框架,比如Core Graphics框架(你可能用到CGColorSpaceRefCGGradientRef)。
編譯器不會自動處理Core Foundation對象的生命周期;如Core Foundation內存管理規則(查看Memory Management Programming Guide for Core Foundation)里所述,你必須調用CFRetainCFRelease(或者其它相關的變體)。
如果你在Objective-CCore Foundation對象之間轉化,你必須要告訴編譯器怎么處理對象的生命周期,你可以使用類型轉換限定符(objc/runtime.h里定義的)或者Core Foundation宏(NSObject.h里定義的)。
__bridge讓對象在Objective-CCore Foundation之間轉換,但是并不轉換所有權。
__bridge_retained或者CFBridgingRetain把一個Objective-C指針轉化成Core Foundation指針,并把對象的所有權轉移給你。你負責調用CFRelease或者相關的方法來釋放對象的所有權。
__bridge_transfer或者CFBridgingRelease把一個非Objective-C指針轉換到Objective-C指針,并把對象的所有權轉交給ARCARC 來負責對象的釋放。
例如,如果你有這樣的代碼:

- (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對象

編譯器理解遵循Cocoa命名規則(查看Advanced Memory Management Programming Guide),并返回Core Foundation類型的Objective-C方法返回。例如,編譯器知道,在iOS中,UIColor類的CGColor方法返回的CGColor對象不會被擁有。但是你還是需要使用正確的類型轉換,如下面的例子所示:

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

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

當你在函數調用時在Objective-CCore Foundation對象之間轉化,你需要告訴編譯器你傳的對象的所有權。Core Foundation對象的所有權規則和那些在Core Foundation內存管理規則中定義的規則一樣(查看Memory Management Programming Guide for 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.
}

#轉化工程到ARC時的常見問題
當遷移已有工程的時候,你可能會遇到許多問題。這里是一些常見的問題和解決方法。

  • 你不能調用retainrelease或者autorelease
    這是一個特性。你也不能這么寫:
    while ([x retainCount]) { [x release]; }

  • 你不能調用dealloc
    典型地,如果你定義一個單例或者在init方法中替換一個對象時,你要調用dealloc。對于單例,使用共享實例模式。在init方法中,你不需要再調用dealloc方法,因為當你覆蓋self的時候對象會自動釋放。

  • 你不能使用NSAutoreleasePool對象。
    使用心得@autoreleasepool{}結構來代替它。它在你的自動釋放池上包裝了一個塊結構,并且會比NSAutoreleasePool快上6倍。@autoreleasepool在非ARC代碼下也能工作。因為@autoreleasepoolNSAutoreleasePool快很多,所有許多“性能問題”可以無腦地把NSAutoreleasePool替換為@autoreleasepool
    遷移工具可以處理簡單的NSAutoreleasePool,但是不能處理復雜情況下的,也不能處理在@autoreleasepool塊里定義變量,然后在后面才使用這個變量的情況。

  • ** ARC需要你在init方法中把[super init]的返回值賦給self
    下面的init方法在
    ARC**中是非法的:
    [super init];
    簡單的修改一下:
    self = [super init];
    完整的修改方法是像上面那樣做,并在繼續前檢查結果是nil:
    self = [super init];
    if (self) {
    ...

  • 你不能自定義retain或者release方法。
    自定義retain或者release方法來實現weak指針。你可能因為一些常見的原因想要自定義:

  • 性能.
    請不要在這么做;現在NSObject類的retainrelease方法比以前更快了。如果你還是發現問題,請提交bug。

  • 定義weak指針系統
    使用__weak來代替。

  • 定義單例。
    使用共享實例模式來代替。或者,使用類方法代替實例方法,可以完全避免分配對象。

  • "Assigned"實例變量變成strong
    ARC之前,實例變量不擁有對象-直接給實例變量賦值一個對象不會保持對象的生命周期。為了使屬性變成strong,你通常可以定義或者synthesized存取器方法來調用合適的內存管理方法;形成鮮明地對比,你可能像下面例子中展示的那樣,定義了一個存取器方法來獲得一個weak屬性。
    @interface MyClass : Superclass {
    id thing; // Weak reference.
    }
    // ...
    @end

    @implementation MyClass
    - (id)thing {
        return thing;
    }
    - (void)setThing:(id)newThing {
        thing = newThing;
    }
    // ...
    @end
    

使用ARC,實例變量默認是strong引用-直接給實例變量賦值一個對象會保持對象的生命周期。遷移工具不能決定什么時候實例變量是weak。為了得到之前一樣的效果,你必須把實例變量標志為weak,或者使用屬性。
@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對象。
    例如下面的代碼不能通過編譯:
    struct X { id x; float y; };
    這是因為x默認是strong,編譯器不能安全地合成讓它能正確工作的代碼。例如,如果你傳了一個指向那種結構體的指針到處理釋放的代碼中,每個id都會在結構體釋放前釋放。編譯器做這些是不可靠的,所以在ARC模式下結構題中strong對象是不允許的。有一些解決方案:
    1. 使用Objective-C來代替機構提。
      這被認為是最好的方案。
  1. 如果使用Objective-C是次級方法,(可能你想使用這些結構體的數組)你可以考慮使用void *來代替。
    這需要使用顯示的轉換,如下面所述。
  2. 把對象引用弄成__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類型)。
    這些在前面的
    處理Toll-Free Bridging*中已經討論過。

常見問題

我怎么理解ARC?它在哪里插入retain/release

不要去考慮retain/release在哪里插入的,去考慮你的程序的算法。考慮你的對象的"strong and weak"指針,考慮對象的擁有關系,考慮哪里可能會retain cycles

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

可能需要。
因為ARC不會自動malloc/free,不會管理Core Foundation對象和file descriptors的生命周期,等等。你還是需要寫一個dealloc方法來釋放這些資源。
你不需要去釋放實例變量,但是你可能需要給系統類和其它沒有使用ARC編譯的代碼調用[self setDelegate:nil]

ARC仍然可能發生retain cycles嗎?

是的。
ARC自動retain/release,并繼承了retain cycles問題。幸運的是,遷移到ARC的代碼很少發生內存泄漏,因為不管指針的屬性是不是retain,都已經自動合成了釋放代碼

block在ARC里怎么工作的?

在ARC模式下,你僅僅需要在棧上傳block,它們就能工作了。你不再需要調用Block Copy。
有一點需要注意,NSString * __block myString在ARC模式下會retain對象,而不是一個可能會變成野指針的指針。為了獲得以前一樣的效果,使用__block NSString * __unsafe_unretained或使用__block NSString * __weak myString(這種更好)。

我可以在OS X雪豹上用ARC開發嗎?

不行。雪豹Xcode4.2不支持ARC,因為它不包含10.7SDK。雪豹Xcode 4.2支持iOS上的ARC,LionXcode 4.2OS X和iOS都支持。這意味著,你需要在Lion系統上buildARC應用,然后在雪豹系統上運行。

在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 **
這些分配的內存必須是零填充。
在釋放數組(memset或者bzero沒用)前必須要把每個元素都設置成nil。
你應該避免memcpy或者realloc

ARC慢嗎?

它取決于你測量什么,但是一般情況下是“不會”。編譯器有效地消除了許多retain/release調用,而且通常Objective-C runtime為ARC的速度作了很多優化。特別是,那些返回一個retain/autoreleased對象的模式會更快,因為當調用方法的代碼是ARC模式下,對象不會被放進自動釋放池。
一個需要注意的問題是在通常的debug配置下優化程序不會運行,因此在-O0下比-Os下可以看到更多retain/release

ARC在ObjC++模式下能工作嗎?

是的。你也可以把strong/weak對象放在類和容器里面。ARC編譯器在拷貝構造函數和析構函數中自動生成retain/release邏輯。

哪些類不支持weak引用?

目前你不能創建下面類的weak引用:
NSATSTypesetter, NSColorSpace, NSFont, NSMenuView, NSParagraphStyle, NSSimpleHorizontalTypesetter, and NSTextView.
注意:另外,在OS X v10.7下,你不能創建NSFontManager, NSFontPanel, NSImage, NSTableCellView, NSViewController, NSWindow, 和 NSWindowControllerweak引用。還有,在OS X v10.7下AVFoundation框架下的類都不支持weak引用。
定義屬性的時候,你應該用assign來代替weak;對于變量,你應該使用__unsafe_unretained代替__weak
另外,在ARC下你不能創建NSHashTable,NSMapTable,或NSPointerArrayweak引用。

當子類化NSCell或其它類時使用NSCopyObject我需要怎么做?

沒有特別需要注意的。ARC代替你來處理以前需要額外顯示地retain的地方。在ARC下,所有的copy方法應該只在實例變量上copy。

我能指定部分文件用ARC編譯嗎?

可以。
當你遷移一個工程到ARC,默認所有的Objective-C文件都被設置了-fobjc-arc編譯標志。你可以用* -fno-objc-arc來為部分類禁用ARC。在Xcode里,在target的Build Phases標簽,打開Compile Sources組來
瀏覽源文件列表。雙擊你想設置的文件,在彈出的框中輸入-fno-objc-arc,然后點擊
Done*。
[圖片上傳失敗...(image-699fca-1574849633608)]

GC(垃圾回收)在Mac被棄用了嗎?

垃圾回收在OS X Mountain Lion v10.8中被棄用了,并且將會在將來的OS X版本中被刪除。自動引用計數是推薦的替換技術。為了幫助遷移已有的應用,Xcode 4.3和之后的ARC遷移工具支持把OS X程序中的垃圾回收遷移到ARC

注意:對于Mac App Store上的應用,蘋果強烈推薦盡可能地用ARC替換垃圾回收機制,因為Mac App Store指南(查看App Store Review Guidelines for Mac Apps)禁止使用棄用的技術。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念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

推薦閱讀更多精彩內容