【译】苹果官方手册:开始使用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阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容