Automatic Reference Counting (ARC)是编译器自动管理Objective-C对象的一个功能,相对于不得不考虑retain和release操作来说,ARC让我们有更多的精力集中在我们应用内有趣的代码、object graphs和对象之间的关系上。
ARC是用过来在编译的时候添加适当的代码来保证对象在有用的时候有效,没有了就不再有效了。从概念上讲,ARC通过调用合适的内存管理方法遵循着和 manual reference counting(MRC)同样的内存管理规则。
为了让编译器产生正确的代码,ARC严格规定了你可以调用的方法和怎样使用toll-free bridging。ARC引入了新的用于对象引用和属性声明的生命周期标识符。
ARC支持Xcode 4.2 for OS X v10.6 and v10.7 (64-bit applications) and for iOS 4 and iOS 5。弱引用不支持OS X v10.6 and iOS 4。
Xcode提供了一个工具用来把非ARC代码自动转换到ARC代码(例如移除retain和release的调用),解决的不能自动迁移的问题。这个工具会把工程中的所有文件都转换成ARC,在使用非ARC方便的情况下,你也可以选择某些文件使用ARC。
不用再记住什么时候该使用retain、release和autorelease了,ARC知道你的对象的使用周期然后在编译的时候加入了合适的内存管理代码。编译器也会为你生成合适的dealloc方法。如果你刚刚使用ARC,那么传统的Cocoa的命名规范在你管理MRC的代码的时候是非常重要的。
一个完整的正确的Person类的实现是这样的:
@interface Person : NSObject @property NSString *firstName; @property NSString *lastName; @property NSNumber *yearOfBirth; @property Person *spouse; @end @implementation Person @end对象的属性默认是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能够确保
oldLastName
在NSLog之前不被释放。
ARC提出了一些新的别的编译器没有的新规则。这些规定是为了能够提供一个完整的可靠的内存管理模型。在一些情况能够很简单的提升性能,在一些情况下能够简化代码或者让你不用处理内存管理。如果你违背这些规则,在编译的时候就会出错,而不是在运行的时候产生错误。
@selector(retain)
,
@selector(release)等等
NSAllocateObject 和
NSDeallocateObject
// Won't work: @property NSString *newTitle; // Works: @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是默认的对象类型。
__strong __weak __unsafe_unretained __autoreleasing
ClassName * qualifier variableName;例如:
MyClass * __weak myWeakReference; MyClass * __unsafe_unretained myUnsafeReference;其他变种写法在技术上是不正确的,当时是被编译器允许,想要了解更过可以看这里: http://cdecl.org/
NSString * __weak string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]]; NSLog(@"string: %@", string);虽然string是在初始化赋值之后使用的,但是在赋值的时候没有强引用指向string,因此它立刻就会被释放。NSLog语句会输出一个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. // ...
MyViewController *myController = [[MyViewController alloc] init…]; // ... myController.completionHandler = ^(NSInteger result) { [myController dismissViewControllerAnimated:YES completion:nil]; }; [self presentViewController:myController animated:YES completion:^{ [myController release]; }];正像是描述的一样,你可以使用__block标识符,在completion handler内设置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 { // Probably nothing... } };在一些情况下如果__weak是不兼容的你可能使用__unsafe__unretained。然而对于循环引用这个可能变得不切实际,因为是非常难得或者不可能验证__unsafe__unretained指针是有效的和指向了同样有问题的对象。
@autoreleasepool { // Code, such as a loop that creates a large number of temporary objects. }这个简单的结构可以允许编译器来推断引用计数状态。在入口处,一个autorelease pool被放进栈。在退出的地方(break,return,goto,fall-through等等)autorelease pool被弹出栈。为了兼容已经存在的代码,如果由于异常退出,autorelease pool是不会被弹出栈的。
- (void)myMethod { NSString *name; NSLog(@"name: %@", name); }NSLog语句将会输出null而不是crashing。
- (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); }
NSArray *colors = <#An array of colors#>; CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);在下面的方法实现中的代码片段,可以注意下CF内存管理规则声明的内存管理方法的使用。
- (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. }
while ([x retainCount]) { [x release]; }
[super init];简单的修改如下:
self = [super init];更好的处理是,检查self是否为空之后再继续操作:
self = [super init]; if (self) { ...
init开头的方法要返回实例对象
id obj = [[NSObject alloc] init];
init方法会初始化alloc方法返回的对象,然后原封不动的返回给调用者。
- (void)initThisObect;
这个方法声明就不对。只有一个方法例外:
- (void)initialize;
属性声明为readonly
属性声明为readonly,而没有添加任何其他方法的话,不管是在类内还是类外都无法对该属性赋值。
@property (nonatomic,readonly,strong) NSString *str;
属性会自动生成一个成员变量_str,一般情况下会生成该成员变量的get和set方法,但是readonly表示只生成了get方法而没有生成set方法,如果我们想在自己的类内对该属性进行赋,我们可以通过以下两种方式声明私有的set方法,然后我们就可以在自己的类内对readonly属性就行赋值啦。
在类的扩展中重新声明一个readwrite属性,只要名字相同该属性同样会对应到_str成员变量:
@property (nonatomic,strong,readwrite) NSString *str;
声明一个私有的set方法:
- (void)setStr:(NSString *)tempStr { if (tempStr != _str) { _str = tempStr; } }
@interface MyClass : Superclass { id thing; // Weak reference. } // ... @end @implementation MyClass - (id)thing { return thing; } - (void)setThing:(id)newThing { thing = newThing; } // ... @end在ARC下,实例变量默认是强引用。赋值给实例变量会延长对象的生命周期。转换工具不知道实例变量是否为弱引用。为了能够拥有和之前一样的功能,你需要使用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
例如下面的代码不能编译:struct X { id x; float y; };因为X默认是请持有的,编译器不能安全的实现所有使之能正确运行的代码。例如通过一些代码传递指针给这些结构体的一些然后结束的时候释放。每一个id不得不在struct释放之前释放。编译器不能依靠这个,所以在ARC下结构体内禁止使用强id。有一下几个解决方案:
- 使用Objective-C对象代替结构体。这是最好的方式。
- 如果Objective-C对象是次优的选项,可以考虑使用void *。需要明确的转换,下面描述。
- 标记对象为__unsafe_unretained。这个方法对下面这个不常见的模式可能是有用的:
struct x { NSString *S; int X; } StaticArray[] = { @"foo", 42, @"bar, 97, ... };可以声明结构体如下:struct x { NSString * __unsafe_unretained S; int X; }
这个可能是有问题的和不安全的,如果结构体下的对象释放了的话。但是对于永远字符串常量是非常有用的。