ARC是一个编译器特征,它提供了对OC对象自动管理内存。ARC让开发者专注于感兴趣的代码和对象的关系,而不用考虑对象的retain和release。
原文地址
ARC在编译时期添加代码,保证对象可用。概念上说,ARC遵循手动引用计数的规则,替开发者在编译时期添加合适的代码。
Xcode4.2(Mac OS 10.6、10.7和iOS4和iOS5)支持ARC,弱引用在10.6和iOS4上不支持。
Xcode提供了一个工具:自动机械得转化为ARC(比如移除retain和release的调用),并帮助开发者解决不能自动迁移的问题。迁移工具将所有文件转化成ARC,开发者也可以对单个文件实施ARC,方便于开发者对某些文件手动引用计数。
@interface Person : NSObject @property NSString *firstName; @property NSString *lastName; @property NSNumber *yearOfBirth; @property Person *spouse; @end @implementation Person @end
- (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不会泄露。
- (void)takeLastNameFrom:(Person *)person { NSString *oldLastname = [self lastName]; [self setLastName:[person lastName]]; NSLog(@"Lastname changed from %@ to %@", oldLastname, [self lastName]); }ARC确保oldLastName在NSLog之前不会被销毁。
// 不正确: @property NSString *newTitle; // 正确: @property (getter=theNewTitle) NSString *newTitle;
// 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是默认property属性
MyClass * __weak myWeakReference; MyClass * __unsafe_unretained myUnsafeReference;
NSString * __weak string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]]; NSLog(@"string: %@", string);尽管string在初始化后被使用了。但是,由于在赋值的时候没有强引用;因此它将立即被销毁。
NSError *error; BOOL OK = [myObject performOperationWithError:&error]; if (!OK) { // Report the error. // ...然而,这种错误是隐含的。
NSError * __strong e;并且声明的方法可能是下面这样(参数是__autoreleasing的):
-(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
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,在完成处理的时候。( 这种方法不推荐,不是每一个程序员都会完整的将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循环,开发者应该使用下面代码:
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... } };
使用ARC,开发者不能直接使用NSAutoreleasePool来管理autorelease pools。而使用@autoreleasepool代替它。
@autoreleasepool { // Code, such as a loop that creates a large number of temporary objects. }
使用ARC,strong、weak和autoreleasing栈变量将隐式初始化为nil,例如:
- (void)myMethod { NSString *name; NSLog(@"name: %@", name); }
开发者使用-fobjc-arc 编译选项开启ARC,还可以对某一个文件使用ARC,便于在使用手动引用计数的文件中使用ARC。对于已经使用ARC的工程,仍可以指定一个文件来关闭ARC通过-fno-objc-arc编译选项。
在多数的Cocoa程序中,开发者需要使用Core Foundaton-style对象,无论是从Core Foundation框架还是从Core foundation的其他框架比如Core Graphics。
编译器不会自动管理Core foundation对象的生命周期。开发者必须根据COreFoundation的内存管理规则,使用CFRetain和CFRelease。
如果开发者在OC和Core foundation两种对象做转换,需要告诉编译器对象的所有权。
● __bridge 不改变所有权的情况下,将OC和Core foundaton对象之间转换。
● __bridge_retained 或者 CFBridgingRetain 或者对象的所有权,将OC和Corefoundaton对象之间转换。开发者仍有责任将释放对象通过CFRelease。
● __bridge_transfer 或者CFBridgingRelease将一个非OC指针,转化为OC指针,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); }
编译器知道返回Core foundaion类的OC的方法,遵循历史的规定。例如,编译器知道从CGColor方法返回的GCColor是不拥有的。开发者必须使用合适的类型去转化。例如:
NSMutableArray *colors = [NSMutableArray arrayWithObject:(id)[[UIColor darkGrayColor] CGColor]]; [colors addObject:(id)[[UIColor lightGrayColor] CGColor]];
当开发者在OC和Core foundation的对象之间转化时,需要告诉编译器传递object的所有权。Core foundation对象所有权的规则在CoreFoundation的内存管理规则中。OC的所有权规则在Advanced Memory Management Programming Guide中。
下面的代码段,数组传递给CGGradientCreateWithCorlors函数需要一个合适的转化。数组是arrayWitshObjects返回的,所以,不要将所有权传递给函数,因此使用__bridge
NSArray *colors = <#An array of colors#>; CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);
- (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.这将强制一个block处于一个自动释放池中。它比NSAutoreleaePool快6倍。@autoreleasepool也在在非ARC模式下工作。
● ARC需要开发者对[super init]赋值给self在init方法中。
下面代码是不可行的
[super init];简单的修正是:
self = [super init];根号的修复是这样的
self = [super init]; if (self) { ...● 不能实现retain和release方法。
实现自定义的retain和release方法打破弱指针。提供了几个常用的 “实现自定义”的原因。
1.性能
不要这么做,NSObject的retain和release已经很快了。如果你发现仍有问题,请提出这个bug
2.实现一个自定义的弱指针系统
使用__weak代替
3.实现单例
使用shared instance pattern代替。或者使用类来代替方法,避免分配对象。
● assigned 将变成strong
在ARC之前,变量的assigned的不会延伸到对象的生命周期。为了让property变成强引用,开发者通常实例化或者synthesized访问器方法,里面是内存管理方法。相比之下,你可以这样实现访问器方法:
@interface MyClass : Superclass { id thing; // Weak reference. } // ... @end @implementation MyClass - (id)thing { return thing; } - (void)setThing:(id)newThing { thing = newThing; } // ... @endARC下,实例中的变量默认是strong引用,assigning一个对象给实例中的变量延伸到对象的生命周期。迁移工具不能决定一个实例变量即将weak。 保持 相同的行为 之前, 你必须 标记 实例变量 是weak , 或声明它的property
@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● 不能使用strong ids在c的数据结构
例如,下面的c结构将不能编译过
struct X { id x; float y; };这是因为x默认是strong retain,但是,在正常运行情况下,编译器不能安全的合成所有需要的代码。比如:如果你传递一个指针给x或者y后,执行了free,每一个id将被release掉,在struct被free之前。编译器不能可靠的做这些。所以strong ids在结构,在ARC中完全不允许。下面是一些解决方案:
1.用OC类代替c结构。这应该是最好的解决办法。
2.如果使用OC类是次要的方法(可能你想要一个高密度的结构数组),那么考虑使用void*代替。这需要使用显示的转化。
3.把对象引用作为__unsafe_unretained。这种方法是半常见的模式,这样是有用的。
struct x { NSString *S; int X; } StaticArray[] = { @"foo", 42, @"bar, 97, ... };开发者这样声明
struct x { NSString * __unsafe_unretained S; int X; }这可能是有问题的,如果对象可以被释放时指针是不安全的,但它是非常有用的东西,被称为是字符串常量。
不要想什么地方调用retain/release,考虑程序本身算法吧。考虑"strong and weak"关系,对象的所有权关系和可能存在的循环引用。
Block能正常工作,当你在栈上传递的时候,比如作为返回值。你无需block copy了。当你传递block在栈下面的时候,添加到arrayWithObjects等需要retain的地方,需要使用[^{ }copy]。
有一件事情需要清除:NSString *__block myString是retain在ARC模式种,不可能是野指针。为了得到以前的行为,使用__block NSString *__unsafe_unretained myString 或者使用__block NSString *__weak myString.
不行。雪豹版本的Xcode4.2不支持ARC。
可以
// 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在释放array的时候(memset和bzero不好使的)
取决于你怎么测量,通常是不慢。编译器消除很多无关紧要的retain/release调用。投入很大的努力在加快OC下的运行环境。尤其是返回一个retian/autoreleased对象,ARC并不是真正将它放到自动释放池。
有一件事需要清除:优化器不在debug模式下。所以想看到更多的retain/release的调用,使用-O0比-Os
是的,甚至可以将strong/weakids在类和容器中。ARC编译器合成retain/release逻辑在拷贝构造函数和析构函数钟。
下面的类不能创建弱引用:
NSATSTypesetter, NSColorSpace, NSFont, NSMenuView, NSParagraphStyle, NSSimpleHorizontalTypesetter, and NSTextView.
对于声明properties时,你应该使用retain代替weak。对于变量你应该使用__unsafe_unretained代替__weak.另外,你不能创建这些类的弱引用:NSHashTable, NSMapTable, or NSPointerArray
没什么特别的。ARC负责的显示retain的场景。ARC模式下,所有的copy方法应该仅仅copy实例变量。
我可以对某个文件不使用ARC么?
是的。当开发者迁移工程到ARC时,对所有OC源文件设置为-fobjc-arc编译选项。开发者可以指定一个文件设置-fno-objc-arc编译选项。
Grabage Collection在10.8上已经过时,将来会重OS X中移除。ARC将是收推荐的替代技术。为了帮助迁移现有程序,ARC迁移工具在Xcode >=4.3 支持将GC迁移到ARC。
点击打开原文