理解 Objective-C 的 ARC
英文原文:Understanding Automatic Reference Counting in Objective-C
|
自动引用计数(Automatic Reference Counting, ARC)把压在程序员们肩头的管理内存的重担卸除了不少,更不用说让跟踪内存泄漏那样的烦心事也少了很多。不过,虽然ARC很棒,我们仍然不能完全把内存管理这回事儿抛在脑后。 这篇文章将要讨论以下方面的问题,帮助大家快速进入ARC的世界。
|
|
发生了什么事?
在ARC出现以前,程序员们只能靠retain/relese/autorelease来确保对象们恰好“坚持”到被需要的那一刻。如果忘了retain,或者多次release某个对象,程序就会发生内存泄漏的问题,甚至直接崩溃。 在Xcode 4.2中,除了语法检查外,Apple的新LLVM编译器还将内存管理的苦差事接了过来,它会检查代码,决定何时释放对象。Apple的文档里是这么定义ARC的:
ARC使内存管理在大部分时候变得如同小事一桩,但我们仍要在决定自己的类如何管理其它对象的引用时承担一些责任。 那么,让我们正式开始吧…… |
|
引用计数: 快速复习手工管理、引用计数式的内存管理在iOS中是这样工作的: 当使用alloc/init(或其它类似方法)创建对象时,随同对象返回的,还有个retainCount,其值为1,表明我们获得了这个对象的所有权。
在对象的alloc/init和release(即放弃对象的所有权)之间,我们可以任意处理它,这很安全,因为系统是无法回收正在使用中的对象的。 将对象加入到自动释放池也是类似,对象会一直存在,直到未来的某个时间我们不再需要它,才会被系统回收。
|
|||||
ARC的工作原理大多数新的iOS程序员都会在引用计数这问题上遇到理解障碍。ARC则是一个编译前的步骤,它为我们的代码自动加上retain/release/autorelease语句。 ARC并不是垃圾收集,而且,引用计数也没有消失,只是变成自动而已。听起来像是事后追加的这么一个功能,不过,只要我们想一想Objective-C有多少功能是通过对源文件的预处理来实现的,就不会这么想了。 当采用ARC后,代码只要这样写:
ARC会自动将它变成:
从下图(来自Apple官方文档)看起来,好像retain/release的数量快赶上真正有用的代码了。当然,这肯定不是熟手的情况,不过可以看成是对新手的保守估计。这些代码跑起来,要跟踪某个内存问题真的是会搞死人。
|
|
在工程中开启ARC如果想开启ARC,只要在工程的Build Settings中设置ARC为YES。在幕后,实际上是设置了-fobj-arc的编译器标识。
ARC施加的新规则如果想用ARC,必须服从一些新规则。 1. 对象的Alloc/Init 创建对象的方法跟以前一样,但你一定不能调用retain/release/autorelease/retainCount。也不能通过selector偷偷地调用它们: 禁止使用@selector(retain)和@selector(release)。 |
|
|
2. dealloc方法 ARC为自动为你调用,一定不能直接调用dealloc。不过,如果你需要释放实例变量以外的资源,还是可以创建自定义的dealloc方法。但在这个方法里,不要调用[super dealloc]。因为ARC会帮你调。 3. 声明的属性 在ARC之前,我们是用@property指令中的assign/retain/copy参数来告诉编译器,如何管理这些属性的内存。用了ARC之后,这些参数就作废了,改用weak/strong这两个参数。 |
|
|
4. C结构中的对象指针 同样禁止使用。文档里建议不要把它们放在结构了,改放到类里去。否则ARC就不认识它们了。可能会出现一些移植上的问题。不过,ARC是可以以文件为单位来关闭的。参考下文的“引入不兼容ARC的代码”。 5. id与void*之间的临时互转 当我们在Core Foundation的C函数和Foundation Kit的Objective-C方法间传递对象时,常常需要进行id和void*两个类型的互转。叫做免费桥接(Toll Free Bridging)。 如果使用ARC,必须在CF对象进入和脱离ARC的控制时,用提示/限定符来告知编译器。限定符有__bridge、__bridge_retain和__bridge_transfer。另外,仍需要用CFRetain和CFRelease来管理Core Foundation的对象。 这一块已经比较高深了,如果你不清楚CF对象是什么,也不需要太烦恼。 |
|
|
6. 以@autoreleasepool代替NSAutoReleasePool 兼容ARC的代码不能再使用NSAutoReleasePool对象,而要改用@autoreleasepool{}块。一个很好的例子:
7. 其它 基于Zone的内存已经没了(在运行时里也没了)。不能再使用NSAllocateObject和NSDeallocateObject。 |
|
ARC限定符 - 声明的属性身为程序员,习惯于做出一些决定,例如把某个量声明为变量还是常量、本地还是全局,等等。因此,在这里,我们也要决定某个属性与其它属性的关系。我们用strong/weak来把这一关系告诉编译器。 强引用 强引用是对某对象的引用,并且能阻止它被回收。换句话说,强引用创建了一个所有关系。在ARC之前,我们这么写:
在ARC下,我们需要这么写,以确保当前实例获得被引用对象的所有权(主人不被回收,它也不能被回收)。
弱引用 弱引用是对某对象的引用,但不能阻止它被回收。换句话说,弱引用并不会创建所有关系。在ARC之前,我们这么写:
在ARC下,我们需要这么写,以确保当前实例没有获得被引用对象的所有权(一般来说,子对象不应该拥有父对象,这时可以用弱引用)。
|
|
ARC限定符 - 常规变量上一节是说明如何管理属性。对于常规变量,则有:
一般来说,我们不太需要使用上面这些限定符。在使用移植工具的时候可能会看到那么几个,但新工程基本上不需要。
来源: http://clang.llvm.org/docs/AutomaticReferenceCounting.html#ownership 注: 我们发现在ARC下,@property中使用“retain”时(而不是“strong”),编译器并不会报错,而且能生成同样结果。但以后可能会变,所以还是用“strong”吧。 |
|
移植到ARCXcode 4.2提供了一个移植ARC的工具,还可以帮你将无法自动移植的代码手工转换过去。 1. 打开不兼容ARC的工程,进入Edit > Refactor > Convert to Objective-C ARC。
2. 选择需要转换的构建目标和文件(在后续步骤排除不需要转换的文件)
3. 运行预查,按下一步。
注: 按下一步后,LLVM编译器会开始构建,以便对工程进行分析。如果有错误,是无法进入下一步的。如果是第一次打开低版本Xcode建立的工程,请先执行清理。 |
|
| 其它翻译版本(1) |
|
4. 检查一下工具建议的修改,并选择是否要排除某个文件。然后按下保存。
注: 如果有文件无法移植,工具也会告诉你。并不是所有文件都需要移植(包括库)。ARC是以文件为基础生效的,可以参考下文,看编译时如何把不需要开启ARC的文件排除在外。 5. 工具会自动设置编译器的标识,开启ARC。可以查看工程的Build Settings确认这一点。 |
|
引入不兼容ARC的代码Apple的文档说,“ARC能以文件为基础,与采用手工引用计数的代码进行交互。你可以在部分文件上使用手工引用计数。” 它的意思是说,我们可以让一部分文件用ARC,另一部分文件不用。下面是将文件批量排除在ARC之外的步骤。在我写下这篇文章的时候,还有许多流行的库都没有用ARC,为了解决这个问题,请按照下面的步骤做:
|
|
我该用ARC吗?如果你是Objective-C的新手,肯定会欢迎ARC。一开始,有太多的东西要学习,有了ARC就不用担心手工计数的问题。随着你开始接触一些已有的库,就会经历一些痛苦(译者注: 目前的第三方库很多不兼容ARC),但只要习惯了将它们排除在ARC之外,就没什么问题了。 如果你不是新手,在没有ARC的年代已经玩的很high,那么可能会觉得“我干嘛要用它!”对你来说,这可能是正确的答案——就目前而言。因为,大多数流行的库都还没转到ARC下,而且ARC对Core Foundation的支持也不好。使用CF类的时候有一些限制,而且,移植代码的时候,为了让免费桥接生效,还需要加上一大堆限定符。 |
|
|
在我看来,目前ARC已经是可以使用的状态了。不过,除非你对它很熟悉,否则还是先用在新工程里会比较好。虽然ARC兼容iOS 4.0以上,但弱引用只在iOS 5.0以上才支持,所以现在还是不要把所有东西都移植过去(有一些相关的文章,请参考最后的“资源”一节) 至于性能方面,早前有报告指出用ARC之后速度会变快,我想可能是由于减少对自动释放的依赖的缘故。虽然这完全可以通过改进代码、更好地使用retain/release来实现,但我觉得重点在于,ARC总是会自动帮你选择最优的方法。 |
|
|
目前为止,还是有很多令人苦恼的东西被移到ARC,但还不是全部。在长周末后我们会离开一段时间以投入到新的项目,但是这是苹果一个“新的”推荐手段,所以你可以肯定以后的设计决策会继续强大ARC并且让大家离开使用手动的引用计数 ARC资源
|
理解 Objective-C 的 ARC的更多相关文章
- 理解Objective C 中id
什么是id,与void *的区别 id在Objective C中是一个类型,一个complier所认可的Objective C类型,跟void *是不一样的,比如一个 id userName, 和vo ...
- CSDN日报20170411 ——《怎样给自己的私活项目标价》
[程序人生]怎样给自己的私活项目标价 作者:瞬息之间 非常早之前讲过我们"怎么接私活的心得技巧".相信非常多同学听了心里痒痒的.据我认识的(无论是现实生活还是网上接触的)朋友来看. ...
- ARC介绍
从Ray Wenderlich的教程中截取了一小段作为对objective c中ARC的介绍,讲得比较清晰,原文有丰富的例子,见此 它是怎么工作的 你大概已经熟悉如何手工管理内存了, 就像这样:如果你 ...
- Objective C Runtime 开发介绍
简介 Objective c 语言尽可能的把决定从编译推迟到链接到运行时.只要可能,它就会动态的处理事情.这就意味着它不仅仅需要一个编译器,也需要一个运行时系统来执行变异好的代码.运行时系统就好像是O ...
- OC - ARC(自动引用计数)
1.什么是自动引用计数? 顾明思义,自动引用计数(ARC,Automatic Reference Counting)是指内存管理中对引用采取自动计数的技术. 在OC中采用ARC机制,让编译器来进行内存 ...
- 内存管理和@property的属性
内存管理和@property的属性 目录 对内存管理的理解 Objective C内存管理方式 内存的管理 对象的所有权和内存管理原则 合理解决内存管理带来的问题 自动释放池 @property的属性 ...
- 什么时候应该使用Autorelease Pool
csdn首发:http://blog.csdn.net/guijiewan/article/details/46470285 Objective c使用ARC之后,一般都不需要再手动调用retain, ...
- iOS燃烧动画、3D视图框架、天气动画、立体相册、微信朋友圈小视频等源码
iOS精选源码 iOS天气动画,包括太阳,云,雨,雷暴,雪动画. 较为美观的多级展开列表 3D立体相册,可以旋转的立方体 一个仪表盘Demo YGDashboardView 一个基于UIScrollV ...
- Java与Swift对比
本文链接:http://blog.csdn.net/msyqmsyq/article/details/53538159 从Java到Swift还是比较简单的,相比Object-C,Swift和Java ...
- Automake
Automake是用来根据Makefile.am生成Makefile.in的工具 标准Makefile目标 'make all' Build programs, libraries, document ...
随机推荐
- 报错找不到org.apache,http...的解决办法
在build.gradle中加入 android { useLibrary 'org.apache.http.legacy' }
- Android抽屉效果 DrawerLayout 入门经验总结
今天试了试这个抽屉布局的效果,结果很崩溃无语 网上很多资料都千篇一律,感觉都有问题,下面总结下几点经验: 先上个效果图: 1. layout 布局文件中怎么写: <android.suppor ...
- CodeForces 670D Magic Powder
二分. 二分一下答案,然后验证一下. #pragma comment(linker, "/STACK:1024000000,1024000000") #include<cst ...
- cocos坐标系及坐标转换
cocos2dx的坐标系是以左下角为(0,0)点, 向右上为正 世界坐标系:相对于屏幕位置的坐标表示 节点坐标系:相对于父节点的坐标表示 可以相互转换: /** * 将世界坐标转换成节点坐标,忽略锚点 ...
- Mybatis一对多查询得不到多方结果
一对多查询:一个年级对应多个学生,现在要查询年级(带学生)信息. 查询结果: [main] INFO com.java1234.service.GradeTest - 查询年级(带学生)[main] ...
- docker命令和后台参数
Docker官方为了让用户快速了解Docker,提供了一个 交互式教程 ,旨在帮助用户掌握Docker命令行的使用方法. Docker 命令行 下面对Docker的命令清单进行简单的介绍,详细内容在后 ...
- linux内存管理(repost)
一 为什么需要使用虚拟内存 大家都知道,进程需要使用的代码和数据都放在内存中,比放在外存中要快很多.问题是内存空间太小了,不能满足进程的需求,而且现在都是多进程,情况更加糟糕.所以提出了虚拟内存,使得 ...
- Online Procurement Auctions for Resource Pooling in Client-Assisted Cloud Storage Systems---INFOCOM 2015
[标题] [作者] [来源] [对本文评价] [why] 存在的问题 [how] [不足] assumption future work [相关方法或论文] [重点提示] [其它]
- python2到python3的转换以及f.write在python3 中的用法
.利用Python内置(Python脚本)工具,帮你自动转换 Python 2.x版本,比如我安装的Python 2.7.2,其在windows下载安装好之后,就自带了相关的一些有用的工具. 其中一个 ...
- redis7--hash set的操作
数据类型Hash(1)介绍hash数据类型存储的数据与mysql数据库中存储的一条记录极为相似Redis本身就类似于Hash的存储结构,分为key-value键值对,实际上它的Hash数据就好像是在R ...






