IOS 5 ARC机制 (一)

在IOS5中最新的特性要属ARC机制了,下面就来详细介绍下ARC:

 

自动内存管理技术(Automatic Reference Counting (ARC))是一个为Objective-C提供内存自动管理的编译期技术。作为取代使用retain和release方式来管理内存的方式,ARC让你在其他代码编写方面可以放入更多精力。下图是两种管理内存方式的对比。

概要说明

ARC的原理是在编译期为每一个对象加入合适的代码,以期能否保证这些对象有合理的生命周期。从概念上来说,ARC通过增加retain,release和autorelease等函数,使得在维护内存计数器方面(相关资料Advanced Memory Management Programming Guide),达到和手动管理内存同样的效果。

为了达到产生正确代码的目的,ARC禁止一些函数的调用和toll-free bridging(相关资料)的使用。ARC也为内存计数器和属性变量引入了新的生命周期。

ARC在MAC OS X 10.6,10.7(64位应用),iOS4和iOS5中被支持,但是在MAC OS X10.6和iOS4中不支持弱引用(Weak references )。

Xcode提供一个能够自动转换工具,可以把手动管理内存的代码来转换成ARC的方式。你也可以为工程中的部分文件指定使用ARC,而另一部分指定为不使用。

你可以参考下面的资料:

ARC概述

作为不得不记得何时调用retain,releaseautorelease的替代,ARC会为你的每一个对象在编译期自动苹果,然后加入合适的函数调用来做内存管理,并且编译器会自动产生合适的dealloc函数。In general, if you’re only using ARC the traditional Cocoa naming conventions are important only if you need to interoperate with code that uses manual reference counting.(上面这句说什么?不懂,不翻译了)

一个Person类在使用ARC的情况下可能的实习如下:

@interface Person : NSObject@property (nonatomic, strong) NSString *firstName;@property (nonatomic, strong) NSString *lastName;@property (nonatomic, strong) NSNumber *yearOfBirth;@property (nonatomic, strong) Person *spouse;@end@implementation Person@synthesize firstName, lastName, yearOfBirth, spouse;@end

(关键字strong参考“ARC Introduces New Lifetime Qualifiers”.本文后面就有)

使用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引入的新规则

ARC能够起作用引入了一些新的规则。这些规则定义了所有的内存管理方方面面,某些规则是为了更好的体验,还有一些规则是为了减少编程人员在内存管理方面的工作。如果你违反这些规则,就会得到编译期的错误,而不是运行期的错误。

  • 你不能显示调用dealloc,不能实现和显示调用retain,release,retainCount和autorelease。

    当然也不能使用@selector(retain), @selector(release)等相关的功能。

    你可能为了管理某些不是实例释放方面的资源而实现一个dealloc。你不用(实际上是不能)释放实例变量,但是你可能需要为系统类实例调用[systemClassInstance setDelegate:nil],这些是ARC管不到的地方。

    在你实现的dealloc函数中不用调用[super dealloc] (实际上不行,因为会得到一个编译错误)。编译器会为父类的实例生成释放代码。

    你可以使用CFRetain, CFRelease或其他相关函数来管理核心功能的实例 (相关内容可以查看“Managing Toll-Free Bridging”).

  • 你不能使用NSAllocateObject和NSDeallocateObject.

    你使用alloc来创建实例;运行期间会管理这些实例的释放。

  • 你不能在C结构中使用实例指针。

    与其使用结构,建议你使用Objective-C类来管理数据。

  • id和void *不能进行隐式转换。

    你必须显式的告诉编译器这个转换的类型。关于这个方面,可以参考“Managing Toll-Free Bridging”.

  • 不能使用NSAutoreleasePool的实例.

    作为替代,ARC提供@autoreleasepool块作为替代。后者提供了更灵活的方式。

  • 你不能使用内存块。

    NSZone已经被废气,它已经给现在的Objective-C运行的时候忽落了。

为了和手动管理内存相兼容,ARC定义了函数和变量命名一条规则:

  • 属性变量的命名不能使用new开始。

ARC引入的新的生存周期修饰符

ARC为实例引入了几个新的生存周期修饰符,特别是自动nil化的弱引用。一个弱引用并不改变它所指向的实例的生命周期,自动nil化的弱引用会在实例被释放后自动变成nil。你应该妥善使用这些修饰符,ARC对于强引用(strong)类型提供很多内存管理,因此弱引用用的更多。

属性变量

新的关键词week和strong被引入,如下所示:

// 下面的同义词是: @property(retain) MyClass *myObject;@property(strong) MyClass *myObject;//下面的声明和"@property(assign) MyClass *myObject;"相似//不过在MyClass的变量被释放的时候,//这个属性变量的值被设置为nil,而不是一个僵尸指针。@property(weak) MyClass *myObject;变量修饰符

你可以使用下列关键词来修饰变量:

__strong__weak__unsafe_unretained__autoreleasing

__strong是缺省的关键词。__weak声明了一个可以自动nil化的弱引用。__unsafe_unretained声明一个弱应用,但是不会自动nil化,也就是说,如果所指向的内存区域被释放了,这个指针就是一个野指针了。__autoreleasing用来修饰一个函数的参数,这个参数会在函数返回的时候被自动释放。

在对栈上分配的变量使用__weak修饰符的时候,必须加以注意。考虑下面的情况:

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

虽然这个NSString的变量初始化后立即赋值给string,但是由于没有其他强引用型的指针指向这个内存地址,所以这个内存地址立即就被释放了,所以后面的log语句显示的是一个空值。

在传值方面也需要注意,下面的代码是会有问题的:

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

 

但是error变量的声明类似于:

NSError * __strong e = nil;

函数的声明如下:

-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;

编译器对代码从新处理成下面这个样子:

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

这个错误存在于本地变量声明为__strong,而参数声明为__autoreleasing,编译器在这个情况下会生成一个临时变量。你可以声明这个本地变量为id __strong *,或者声明这个变量为__autoreleasing。

译者注:上面的这段大家再研究一下。我觉得有问题。

posted @ 2012-11-22 11:54  GreyWolf  阅读(208)  评论(0编辑  收藏  举报