四、使用ARC需要注意的问题
1、ARC中读写相关的属性
读写相关的属性有 readwrite 和 readonly 两种,如果使用ARC之后,我么需要注意一下 readonly 属性的使用。
比如下面的变量声明:
- @property (nonatomic, readonly) NSString *name;
“ARC forbids synthesizing a property of an Objective-C object with unspecified ownership or storage attribute”
针对上述错误,需要做出修改:
- @property (nonatomic, strong, readonly) NSString *name;
2、ARC和IOS4
ARC在IOS4是没有 __weak 关键字的,需要使用 unsafe_unretained来代替。
3、ARC中的内存泄露
使用了ARC也并不意味着我们的工程里面不会出现内存泄露了。在ARC机制下,最常见导致内存泄露的是循环强引用。容易出现的场合有:
①Outlet类型指针
Outlet类型的指针变量应该用weak属性来声明
②委托
一定要将delegate的属性设为weak,原因我就不解释了,实在不明白,请猛击这里
③block
下面这段代码,在MRC条件下是没有问题的:
- MyViewController * __block myController = [[MyViewController alloc] init…];
- // ...
- myController.completionHandler = ^(NSInteger result) {
- [myController dismissViewControllerAnimated:YES completion:nil];
- };
但是在ARC条件下,就回内存泄露,导致myController指向的对象无法释放。
原因是,__block id x声明的变量x用于block中时,MRC条件下是不会增加x的引用计数,但是在ARC条件下,会使x得引用计数加一,请各位务必注意!!!!!!!!!!!!
上述问题代码有以下几种解决方案:
方案一:
- MyViewController * __block myController = [[MyViewController alloc] init…];
- // ...
- myController.completionHandler = ^(NSInteger result) {
- [myController dismissViewControllerAnimated:YES completion:nil];
- myController = nil;
- };
方案二:
- 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...
- }
- };
④定时器
定时器也是非常容易产生内存泄露的地方。比如下面的代码:
- @implementation AnimatedView
- {
- NSTimer *timer;
- }
- - (id)initWithCoder:(NSCoder *)aDecoder
- {
- 
-  if ((self = [super initWithCoder:aDecoder])){
- timer = [NSTimer scheduledTimerWithT imeInterval:0.1
- target:self
- selector:@selector(handleTimer:)
- userInfo:nil
- repeats:YES];
- }
- return self;
- }
- - (void)dealloc
- {
- [timer invalidate];
- }
- - (void)handleTimer:(NSTimer*)timer
- {
- //do something
- }
解决方法是将timer的属性设置为__weak。
4、@autoreleasepool和NSAutoreleasePool
ARC中是不支持使用NSAutoreleasePool的,但是可以使用@autoreleasepool代替。@autoreleasepool既可以用在ARC环境中,也可以用在非ARC环境中,而且效率要比前者高,苹果官网中是这样描述的:
- ARC provides @autoreleasepool blocks instead. These have an advantage of being more efficient than NSAutoreleasePool.
5、还需要声明@property接口吗
在 ARC 之前,开发者经常会在.m 实现文件中使用 class extension 来定义 private property,如下:
这样做主要是简化实例对象的手动内存管理,让 property 的 setter 方法自 动管理原来对象的释放,以及新对象的 retain。但是有了 ARC,这样的代码就不 再需要了。一般来说,仅仅为了简化内存管理,是不再需要使用 property 的, 虽然你仍然可以这样做,但直接使用实例变量是更好的选择。只有那些属于 public 接口的实例变量,才应该定义为 property。
6、使用ARC需要遵守的新规则
①不要在dealloc方法中调用[super dealloc];
②不能使用 retain/release/retainCount/autorelease
③不能使用 NSAllocateObject/NSDeallocateObject
④不能使用 NSZone
⑤Objective-C 对象不能作为C语言结构体(struct/union)的成员
7、ARC只会帮我们管理Objective-C对象的声明周期
其它的,像Core Foundation对象的类型不在ARC的管理范围内,如何处理它们可以参见:Managing Toll-Free Bridging
8、ARC和非ARC文件的混合使用
介绍这部分的文章网上太多了,就是在build phases中修改compiler Flags值,根据需要修改成:-fobjc-arc或者-fno-objc-arc
9、编写兼容ARC和非ARC的通用代码
由于ARC和非ARC短期内仍会共存,写出兼容它们的通用代码还是很有必要的,尤其是开发一些第三方库的时候。利用下面这部分宏就可以轻松实现兼容,代码来自:优快云博主iBright
- #if !defined(__clang__) || __clang_major__ < 3
- #ifndef __bridge
- #define __bridge
- #endif
- #ifndef __bridge_retain
- #define __bridge_retain
- #endif
- #ifndef __bridge_retained
- #define __bridge_retained
- #endif
- #ifndef __autoreleasing
- #define __autoreleasing
- #endif
- #ifndef __strong
- #define __strong
- #endif
- #ifndef __unsafe_unretained
- #define __unsafe_unretained
- #endif
- #ifndef __weak
- #define __weak
- #endif
- #endif
- #if __has_feature(objc_arc)
- #define SAFE_ARC_PROP_RETAIN strong
- #define SAFE_ARC_RETAIN(x) (x)
- #define SAFE_ARC_RELEASE(x)
- #define SAFE_ARC_AUTORELEASE(x) (x)
- #define SAFE_ARC_BLOCK_COPY(x) (x)
- #define SAFE_ARC_BLOCK_RELEASE(x)
- #define SAFE_ARC_SUPER_DEALLOC()
- #define SAFE_ARC_AUTORELEASE_POOL_START() @autoreleasepool {
- #define SAFE_ARC_AUTORELEASE_POOL_END() }
- #else
- #define SAFE_ARC_PROP_RETAIN retain
- #define SAFE_ARC_RETAIN(x) ([(x) retain])
- #define SAFE_ARC_RELEASE(x) ([(x) release])
- #define SAFE_ARC_AUTORELEASE(x) ([(x) autorelease])
- #define SAFE_ARC_BLOCK_COPY(x) (Block_copy(x))
- #define SAFE_ARC_BLOCK_RELEASE(x) (Block_release(x))
- #define SAFE_ARC_SUPER_DEALLOC() ([super dealloc])
- #define SAFE_ARC_AUTORELEASE_POOL_START() NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- #define SAFE_ARC_AUTORELEASE_POOL_END() [pool release];
- #endif
后记:
ARC是很好,但现在处于过渡期,不少第三方库和养老代码都还不支持,所以直接将项目从MRC转换成ARC还是有一定风险的。不过ARC肯定是是今后的趋势,从两个地方可以看出来:
一是Xcode5在创建新工程的时候,ARC是强制选择的;
二是在MAC OS X上可以使用的垃圾回收机制,从OS X10.8山狮开始,垃圾回收机制已经不再推荐使用了,苹果官网原话如下:
- Garbage collection is deprecated in OS X Mountain Lion v10.8, and will be removed in a future version of OS X. Automatic Reference Counting is the recommended replacement technology.
再开发新项目,我只会选择使用ARC,你呢,准备好了吗?
参考文档:
1、Transitioning to ARC Release Notes
2、泰然网