IOS 5 ARC机制 (二)

使用生存周期修饰符来避免强引用环

你可以使用这些生存周期修饰符来避免强引用环。例如,如果程序中存在父类,子类,并且父类需要还有指向子类的指针,那么就有可能存在这样的引用环,反之亦然,那么你可以让父类中的指针是强类型,子类中指向父类的指针是弱类型。其他的情况需要更精巧的处理,特别是在块代码中的使用。

在手动管理模式下,__block id x; 对x的内存计数加一。在ARC模式下,__block id x;缺省的对x的内存计数加一。为了是这样的代码能够在ARC模式下能够继续工作,你可以使用__unsafe_unretained __block id x;. 就像__unsafe_unretained名字暗示的那样,它也会有一定的问题 (因为它可能被称为野指针) 。有两种方式避免上面的问题,一种是使用__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];}];

下面是ARC模式的替代代码,在__block里面赋值为nil:

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

或者使用__weak修饰词,下面的代码就是一个演示:

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

在某些情况下,你可以使用:

MyViewController *myController = [[MyViewController alloc] init…];// ...__weak MyViewController *weakMyController = myController;myController.completionHandler = ^(NSInteger result) {MyViewController *strongMyController = weakMyController;if (strongMyController) {// ...[strongMyController dismissViewControllerAnimated:YES completion:nil];// ...}else {// Probably nothing...}};

在某些情况下,比如类不支持__weak模式,那么你可以使用__unsafe_unretained。但是你必须保证这个变量的生命周期,既保证它不能称为野指针。

ARC提供了新的语法来替代自动释放池

在ARC模式下,你不能直接使用NSAutoReleasePool来实现自动释放池,作为替代,ARC提供了新的语法:

@autoreleasepool {// Code, such as a loop that creates a large number of temporary objects.}

这个新语句准许编译器管理自动释放的状态。

在进入点,自动释放池会压入栈中,当正常状态退出的时候,自动释放池会被处栈,(break, return, goto, fall-through等情况下)。但是如果在异常的情况下,自动释放池就不会有出栈。

这个语法在所有的Objective-C模式下都能工作。它比NSAutoReleasePool更有效率。你需要把所有原先的代码都改成新的模式。

ARC在Outlet变量方面的规定

这部分的规定只有一条,就是Outlet变量必须使用弱引用。

关于这个,请参考Resource Programming Guide中的“Nib Files”节。

栈上的变量被初始化为nil

使用ARC,栈上的变量,不论是强引用,还是弱引用,自动释放等类型的,都是会被隐式的初始化为nil,比如下面的代码:

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

上面的代码不会出现运行崩溃,但是会打印空值。

使用编译选项来开关ARC

使用-fobjc-arc打开ARC. 对于单个文件,你可以使用-fno-objc-arc来为某个你希望使用手动管理内存的文件来禁用ARC。

Xcode4.2再MAC OS X10.6和10.7(64位应用)和iOS 4,iOS5支持ARC,Mac OS X 10.6和iOS4不支持弱引用,Xcode4.1以及以前的版本不支持ARC。

管理无损桥接(Toll-Free Bridging)

Toll-Free Bridging,这个词太难翻译了,我找了很久也没有找到一个合适的翻译,大家就凑合着看吧

在很多Cocoa程序中,你会用到核心基础框架提供的类的实例,不论它是来自核心框架本身 (比如CFArrayRef 或CFMutableDictionaryRef)或者从核心基础框架延伸出的框架,比如核心图形框架(比如CGColorSpaceRefCGGradientRef)。

在ARC模式下,编译器并不制动管理这些核心功能类的实例,你必须自己调用CFRetainCFRelease (或者变量的特殊定义的函数)来符合核心基础的内存管理(参考 Memory Management Programming Guide for Core Foundation)。

如果你需要基础核心类实例和Objective-C类实例的转换,你需要使用一个转换(定义在objc/runtime.h)或者基础核心类型宏(定义在NSObject.h中)告诉编译器这个变量的所属关系。

  1. 如果是函数调用,请使用宏,比如CFBridgingRetain。这些宏使用新的修改算子来做id和void*之间的转换,并且告诉编译器关于这个void*的内存计数器的值。

    NS_INLINE CFTypeRef CFBridgingRetain(id X) {return (__bridge_retain CFTypeRef)X;}NS_INLINE id CFBridgingRelease(CFTypeRef X) {return (__bridge_transfer id)X;}

    仍然需要一个无操作运算符(__bridge)。

  2. 如果你使用C风格的转换,你可以使用直接转换:

    id my_id;CFStringRef my_cfref;...NSString *a = (__bridge NSString*)my_cfref; // Noop cast.CFStringRef b = (__bridge CFStringRef)my_id; // Noop cast....NSString *c = (__bridge_transfer NSString*)my_cfref; // -1 on the CFRefCFStringRef d = (__bridge_retained CFStringRef)my_id; // returned CFRef is +1

编译器处理从Cocoa函数返回的CF实例

编译器理解从核心功能函数返回遵循命名转换的Objective-C实例的从属关系 (参考Advanced Memory Management Programming Guide)。例如,编译器知道,在iOS中,从CGColor函数返回的CGColor是不从属于任何实例的。下面这个例子是一个演示:

- (id)initWithCoder:(NSCoder *)aDecoder {self = [super initWithCoder:aDecoder];if (self) {CAGradientLayer *gradientLayer = (CAGradientLayer *)[self layer];gradientLayer.colors = [NSArray arrayWithObjects:[[UIColor darkGrayColor] CGColor],[[UIColor lightGrayColor] CGColor], nil];gradientLayer.startPoint = CGPointMake(0.0, 0.0);gradientLayer.endPoint = CGPointMake(1.0, 1.0);}return self;}

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