分享

ARC使用心得(二)

 方海龙的书馆 2014-09-03

四、使用ARC需要注意的问题

1、ARC中读写相关的属性

读写相关的属性有 readwrite 和 readonly 两种,如果使用ARC之后,我么需要注意一下 readonly 属性的使用。
比如下面的变量声明:

  1. @property (nonatomic, readonly) NSString *name;  
前边说过,NSString类型的指针的默认属性是strong,按理说应该不需要持有所有权了,但是在ARC有效的情况下,将出现下面的错误信息 :
“ARC forbids synthesizing a property of an Objective-C object with unspecified ownership or storage attribute”
针对上述错误,需要做出修改:
  1. @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条件下是没有问题的:

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

但是在ARC条件下,就回内存泄露,导致myController指向的对象无法释放。

原因是,__block id x声明的变量x用于block中时,MRC条件下是不会增加x的引用计数,但是在ARC条件下,会使x得引用计数加一,请各位务必注意!!!!!!!!!!!!

上述问题代码有以下几种解决方案:

方案一:

  1. MyViewController * __block myController = [[MyViewController alloc] init…];  
  2. // ...  
  3. myController.completionHandler =  ^(NSInteger result) {  
  4.     [myController dismissViewControllerAnimated:YES completion:nil];  
  5.     myController = nil;  
  6. };  
最简单的解决办法,在block中使用完myController时,是它指向nil,没有strong类型的指针指向myController指向的对象时,该对象就回被释放掉。

方案二:

  1. MyViewController *myController = [[MyViewController alloc] init…];  
  2. // ...  
  3. MyViewController * __weak weakMyViewController = myController;  
  4. myController.completionHandler =  ^(NSInteger result) {  
  5.     [weakMyViewController dismissViewControllerAnimated:YES completion:nil];  
  6. };  
该方案使用了一个临时的__weak类型的指针weakMyViewController,在block中使用该指针不会导致引用计数加一,但却存在隐患,当该对象在外部被释放时,block里面执行的操作就无效了。下面的方案三可以解决这个问题。

方案三:

  1. MyViewController *myController = [[MyViewController alloc] init…];  
  2. // ...  
  3. MyViewController * __weak weakMyController = myController;  
  4. myController.completionHandler =  ^(NSInteger result) {  
  5.     MyViewController *strongMyController = weakMyController;  
  6.     if (strongMyController) {  
  7.         // ...  
  8.         [strongMyController dismissViewControllerAnimated:YES completion:nil];  
  9.         // ...  
  10.     }  
  11.     else {  
  12.         // Probably nothing...  
  13.     }  
  14. };  
即在block中使用myController对象之前再声明一个临时的strong类型的指针,指向weak类型的指针,这时strongMyController指针就变成了有效的强引用,其指向的对象就能保证不被释放掉。

④定时器

定时器也是非常容易产生内存泄露的地方。比如下面的代码:

  1. @implementation AnimatedView  
  2. {  
  3.   NSTimer *timer;  
  4. }  
  5.   
  6. - (id)initWithCoder:(NSCoder *)aDecoder  
  7. {  
  8.   
  9.   if ((self = [super initWithCoder:aDecoder])){  
  10.     timer = [NSTimer scheduledTimerWithT    imeInterval:0.1  
  11.     target:self  
  12.     selector:@selector(handleTimer:)  
  13.     userInfo:nil  
  14.     repeats:YES];  
  15.   }  
  16.   return self;  
  17. }  
  18. - (void)dealloc  
  19. {   
  20.   [timer invalidate];  
  21. }  
  22. - (void)handleTimer:(NSTimer*)timer  
  23. {  
  24.   //do something  
  25. }  
乍一看这段代码没啥问题,但是运行起来才发现dealloc方法是不会被调用的,self有一个timer的强引用,timer又有一个self的强引用,典型的循环引用!

解决方法是将timer的属性设置为__weak。

4、@autoreleasepool和NSAutoreleasePool

ARC中是不支持使用NSAutoreleasePool的,但是可以使用@autoreleasepool代替。@autoreleasepool既可以用在ARC环境中,也可以用在非ARC环境中,而且效率要比前者高,苹果官网中是这样描述的:

  1. 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短期内仍会共存,写出兼容它们的通用代码还是很有必要的,尤其是开发一些第三方库的时候。利用下面这部分宏就可以轻松实现兼容,代码来自:CSDN博主iBright

  1. #if !defined(__clang__) || __clang_major__ < 3    
  2.     #ifndef __bridge    
  3.         #define __bridge    
  4.     #endif    
  5.     
  6.     #ifndef __bridge_retain    
  7.         #define __bridge_retain    
  8.     #endif    
  9.     
  10.     #ifndef __bridge_retained    
  11.         #define __bridge_retained    
  12.     #endif    
  13.     
  14.     #ifndef __autoreleasing    
  15.         #define __autoreleasing    
  16.     #endif    
  17.     
  18.     #ifndef __strong    
  19.         #define __strong    
  20.     #endif    
  21.     
  22.     #ifndef __unsafe_unretained    
  23.         #define __unsafe_unretained    
  24.     #endif    
  25.     
  26.     #ifndef __weak    
  27.         #define __weak    
  28.     #endif    
  29. #endif    
  30.     
  31. #if __has_feature(objc_arc)    
  32.     #define SAFE_ARC_PROP_RETAIN strong    
  33.     #define SAFE_ARC_RETAIN(x) (x)    
  34.     #define SAFE_ARC_RELEASE(x)    
  35.     #define SAFE_ARC_AUTORELEASE(x) (x)    
  36.     #define SAFE_ARC_BLOCK_COPY(x) (x)    
  37.     #define SAFE_ARC_BLOCK_RELEASE(x)    
  38.     #define SAFE_ARC_SUPER_DEALLOC()    
  39.     #define SAFE_ARC_AUTORELEASE_POOL_START() @autoreleasepool {    
  40.     #define SAFE_ARC_AUTORELEASE_POOL_END() }    
  41. #else    
  42.     #define SAFE_ARC_PROP_RETAIN retain    
  43.     #define SAFE_ARC_RETAIN(x) ([(x) retain])    
  44.     #define SAFE_ARC_RELEASE(x) ([(x) release])    
  45.     #define SAFE_ARC_AUTORELEASE(x) ([(x) autorelease])    
  46.     #define SAFE_ARC_BLOCK_COPY(x) (Block_copy(x))    
  47.     #define SAFE_ARC_BLOCK_RELEASE(x) (Block_release(x))    
  48.     #define SAFE_ARC_SUPER_DEALLOC() ([super dealloc])    
  49.     #define SAFE_ARC_AUTORELEASE_POOL_START() NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  
  50.     #define SAFE_ARC_AUTORELEASE_POOL_END() [pool release];    
  51. #endif   
怎么使用就不用介绍了吧!

后记:

ARC是很好,但现在处于过渡期,不少第三方库和养老代码都还不支持,所以直接将项目从MRC转换成ARC还是有一定风险的。不过ARC肯定是是今后的趋势,从两个地方可以看出来:

一是Xcode5在创建新工程的时候,ARC是强制选择的;

二是在MAC OS X上可以使用的垃圾回收机制,从OS X10.8山狮开始,垃圾回收机制已经不再推荐使用了,苹果官网原话如下:

  1. 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、泰然网

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多