ARC mark

本文深入介绍了iOS5中新增的自动引用计数(ARC)特性,包括其如何简化内存管理、属性property的使用变化、dealloc方法的调整等内容。

iOS 5最显著的变化就是增加了Automatic Reference Counting(自动引用计数)。ARC是新LLVM 3.0编译器的特性,完全消除了手动内存管理的烦琐。在你的项目中使用ARC是非常简单的,所有的编程都和以前一样,除了你不再调用retain, release, autorelease。启用ARC之后,编译器会自动在适当的地方插入适当的retain, release, autorelease语句。你不再需要担心内存管理,因为编译器为你处理了一切。注意ARC是编译器特性,而不是iOS运行时特性(除了weak指针系统),它也不是其它语言中的垃圾收集器。因此ARC和手动内存管理性能是一样的,有些时候还能更加快速,因为编译器还可以执行某些优化。 


指针保持对象的生命 

ARC的规则非常简单:只要还有一个变量指向对象,对象就会保持在内存中。当指针指向新值,或者指针不再存在时,相关联的对象就会自动释放。这条规则对于实例变量、synthesize属性、本地变量都是适用的。 


以下代码在ARC之前是不可能的,在手动内存管理中,从Array中移除一个对象会使对象不可用,对象不属于Array时会立即被释放。随后NSLog()打印该对象就会导致应用崩溃。 

id obj = [array objectAtIndex:0]; 

[array removeObjectAtIndex:0]; 

NSLog(@"%@", obj); 

ARC中这段代码是完全合法的,因为obj变量是一个strong指针,它成为了对象的拥有者,从Array中移除该对象也不会导致对象被释放。 


ARC也有一些限制。首先ARC只能工作于Objective-C对象,如果应用使用了Core Foundationmalloc()/free(),此时需要你来管理内存。此外ARC还有其它一些更为严格的语言规则,以确保ARC能够正常地工作。不过总的来说,ARC无疑利大于弊! 


属性property 

对于.h头文件,Xcode主要是将属性定义由retain变为strong,这些属性是类对外的接口,因此定义在.h文件中: 

@property (retain, nonatomic) 

变为 

@property (strong, nonatomic) 

在ARC之前,开发者经常会在.m实现文件中使用class extension来定义private property,如下: 

@interface MainViewController () 

@property (nonatomic, retain) NSMutableArray *searchResults; 

@property (nonatomic, retain) SoundEffect *soundEffect; 

@end 

这样做主要是简化实例对象的手动内存管理,让property的setter方法自动管理原来对象的释放,以及新对象的retain。但是有了ARC,这样的代码就不再需要了。一般来说,仅仅为了简化内存管理,是不再需要使用property的,虽然你仍然可以这样做,但直接使用实例变量是更好的选择。只有那些属于public接口的实例变量,才应该定义为property。 

我们可以直接在.m类实现中定义private实例变量: 

@implementation MainViewController 

NSOperationQueue *queue; 

NSMutableString *currentStringValue; 

NSMutableArray *searchResults; 

SoundEffect *soundEffect; 

我们在使用时,虽然没有定义property,也可以直接 

[self.soundEffect play]; 

如果你觉得这很别扭,也可以使用 

[[self soundEffect] play]; 

如果你还是觉得应该定义property,那就定义一个吧,反正也没什么害处。 

作为property的最佳实践,如果你定义某个东西为property,则你应该在任何地方都按属性来使用它。唯一例外的是init方法、自定义的getter和setter方法。因此很多时候我们会这样写synthesize语句: 

@synthesize propertyName = _propertyName; 

实际上_propertyName实例变量甚至可以不定义,编译器会自动为property定义 "_*" 的实例变量 


property的修饰符总结如下: 

strong:等同于"retain",属性成为对象的拥有者 

weak:属性是weak pointer,当对象释放时会自动设置为nil,记住Outlet应该使用Weak 

unsafe_unretained:等同于之前的"assign",只有iOS 4才应该使用 

copy:和之前的copy一样,复制一个对象并创建strong关联 

assign:对象不能使用assign,但原始类型(BOOL、int、float)仍然可以使用 


readonly property 

在ARC之前,我们可以如下定义一个readonly property: 

@property (nonatomic, readonly) NSString *result; 

这会隐式地创建一个assign property,这种用法对于readonly值来说是适当的。毕竟你何必对只读数据进行retain呢?但上面在ARC中会报错: 

"ARC forbids synthesizing a property of an Objective-C object with unspecified ownership or storage attribute" 

你必须显式地使用strong, weak 或 unsafe_unretained,多数情况下使用strong是正确的选择: 

@property (nonatomic, strong, readonly) NSString *result; 

对于readonly property,我们应该总是使用self.propertyName来访问实例变量(除了init和自定义的getter和setter方法)。否则直接修改实例变量会混淆ARC并导致奇怪的Bug。正确的方法是使用class extension重新定义property为readwrite: 

.h文件: 

@interface WeatherPredictor 

@property (nonatomic, strong, readonly) NSNumber *temperature; 

@end 

.m文件: 

@interface WeatherPredictor () 

@property (nonatomic, strong, readwrite) NSNumber *temperature; 

@end 


dealloc方法 

另外启用ARC之后,dealloc方法在大部分时候都不再需要了,因为你不能调用实例对象的release方法,也不能调用[super dealloc]。假如原先的dealloc方法只是释放这些对象,Xcode就会把dealloc方法完全移除。你不再需要手动释放任何实例变量。 

如果你的dealloc方法处理了其它资源(非内存)的释放,如定时器、Core Foundation对象,则你仍然需要在dealloc方法中进行手动释放,如CFRelease(), free()等。这时Xcode会保留dealloc方法,但是移除所有的release和[super dealloc]调用。如下: 

- (void)dealloc 

AudioServicesDisposeSystemSoundID(soundID); 


AutoreleasePool 

ARC仍然保留了AutoreleasePool,但是采用了新的Block语法,于是我们的main函数会如下修改: 

NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 

int retVal = UIApplicationMain(argc, argv, nil, 

NSStringFromClass([AppDelegate class])); 

[pool release]; 

return retVal; 

会修改变: 

@autoreleasepool { 

int retVal = UIApplicationMain(argc, argv, nil, 

NSStringFromClass([AppDelegate class])); 

return retVal; 


unsafe_unretained 

除了strong和weak,还有另外一个unsafe_unretained关键字,一般你不会使用到它。声明为unsafe_unretained的变量或property,编译器不会为其自动添加retain和release。unsafe_unretained只是为了兼容iOS 4,因为iOS 4没有weak pointer system。 

"unsafe"表示这种类型的指针可能指向不存在的对象。使用这样的指针很可能导致应用崩溃,启用 NSZombieEnabled 调试工具可以查找这种类型的错误。 

@property (nonatomic, unsafe_unretained) IBOutlet UITableView *tableView; 

@property (nonatomic, unsafe_unretained) IBOutlet UISearchBar *searchBar; 

运行应用并模拟内存不足警告时,可以看到以下输出: 

Artists[982:207] Received memory warning. 

Artists[982:207] *** -[UITableView retain]: message sent to deallocated instance 0x7033200 

应用崩溃了!unsafe_unretained指针并没有对象的所有权。意味着UITableView不会被这个指针保持生命,在viewDidUnload被调用之前UITableView就已经被释放(UITableView唯一的拥有者是main view)。如果这是一个真正的weak指针,则它的值会被自动设置为nil,这也正是"zeroing" weak pointer的优点。 

unsafe_unretained 指针和 weak 指针不一样的是,当相关联的对象释放时,指针不会被设置为nil,因此它实际上指向不存在的对象。有时候你的应用看上去没有问题,但更大的可能是应用会崩溃。 

注意:你需要选择 Product->Edit Scheme->Diagnostics,并启用zombies调试选项,才能立即看到应用的崩溃。 




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值