ARC入门

手把手教你ARC——iOS/Mac开发ARC入门和使用

本文部分实例取自iOS 5 Toturail一书中关于ARC的教程和公开内容,仅用于技术交流和讨论。请不要将本文的部分或全部内容用于商用,谢谢合作。

欢迎转载本文,但是转载请注明本文出处:http://www.onevcat.com/2012/06/arc-hand-by-hand/

本文适合人群:对iOS开发有一定基础,熟悉iOS开发中内存管理的Reference Counting机制,对ARC机制有听闻很向往但是一直由于种种原因没有使用的童鞋。本文将从ARC机理入手对这个解放广大iOS开发者的伟大机制进行一个剖析,并逐步引导你开始使用ARC。一旦习惯ARC,你一定会被它的简洁高效所征服。

写在开头

虽然距离WWDC2011和iOS 5已经快一年时间,但是很多开发者并没有利用新方法来提高自己的水平,这点在ARC的使用上非常明显(特别是国内,基本很少见到同行转向ARC)。我曾经询问过一些同行为什么不转向使用ARC,很多人的回答是担心内存管理不受自己控制..其实我个人认为这是对于ARC机制了解不足从而不自信,所导致的对新事物的恐惧。而作为最需要“追赶时髦”的职业,这样的心态将相当不利。谨以此文希望能清楚表述ARC的机理和用法,也希望能够成为现在中文入门教学缺失的补充。


什么是ARC

Automatic Reference Counting,自动引用计数,即ARC,可以说是WWDC2011和iOS5所引入的最大的变革和最激动人心的变化。ARC是新的LLVM 3.0编译器的一项特性,使用ARC,可以说一举解决了广大iOS开发者所憎恨的手动内存管理的麻烦。

在工程中使用ARC非常简单:只需要像往常那样编写代码,只不过永远不写retain,releaseautorelease三个关键字就好~这是ARC的基本原则。当ARC开启时,编译器将自动在代码合适的地方插入retainreleaseautorelease,而作为开发者,完全不需要担心编译器会做错(除非开发者自己错用ARC了)。好了,ARC相当简单吧~到此为止,本教程结束。

等等…也许还有其他问题,最严重的问题是“我怎么确定让ARC来管理不会出问题?”或者“用ARC会让程序性能下降吧”。对于ARC不能正处理内存管理的质疑自从ARC出生以来就一直存在,而现在越来越多的代码转向ARC并取得了很好的效果,这证明了ARC是一套有效的简化开发复杂程度的机制,另外通过研究ARC的原理,可以知道使用ARC甚至能提高程序的效率。在接下来将详细解释ARC的运行机理并且提供了一个step-by-step的教程,将非ARC的程序转换为ARC。


ARC工作原理

手动内存管理的机理大家应该已经非常清楚了,简单来说,只要遵循以下三点就可以在手动内存管理中避免绝大部分的麻烦:

如果需要持有一个对象,那么对其发送retain 如果之后不再使用该对象,那么需要对其发送release(或者autorealse) 每一次对retain,alloc或者new的调用,需要对应一次release或autorealse调用

初学者可能仅仅只是知道这些规则,但是在实际使用时难免犯错。但是当开发者经常使用手动引用计数 Manual Referecen Counting(MRC)的话,这些规则将逐渐变为本能。你会发现少一个release的代码怎么看怎么别扭,从而减少或者杜绝内存管理的错误。可以说MRC的规则非常简单,但是同时也非常容易出错。往往很小的错误就将引起crash或者OOM之类的严重问题。

在MRC的年代里,为了避免不小心忘写release,Xcode提供了一个很实用的小工具来帮助可能存在的代码问题(Xcode3里默认快捷键Shift+A?不记得了),可以指出潜在的内存泄露或者过多释放。而ARC在此基础上更进一步:ARC是Objective-C编译器的特性,而不是运行时特性或者垃圾回收机制,ARC所做的只不过是在代码编译时为你自动在合适的位置插入releaseautorelease,就如同之前MRC时你所做的那样。因此,至少在效率上ARC机制是不会比MRC弱的,而因为可以在最合适的地方完成引用计数的维护,以及部分优化,使用ARC甚至能比MRC取得更高的运行效率。

ARC机制

学习ARC很简单,在MRC时代你需要自己retain一个想要保持的对象,而现在不需要了。现在唯一要做的是用一个指针指向这个对象,只要指针没有被置空,对象就会一直保持在堆上。当将指针指向新值时,原来的对象会被release一次。这对实例变量,synthesize的变量或者局部变量都是适用的。比如

NSString *firstName = self.textField.text;

firstName现在指向NSString对象,这时这个对象(textField的内容字符串)将被hold住。比如用字符串@“OneV"作为例子(虽然实际上不应该用字符串举例子,因为字符串的retainCount规则其实和普通的对象不一样,大家就把它当作一个普通的对象来看吧…),这个时候firstName持有了@"OneV"。

当然,一个对象可以拥有不止一个的持有者(这个类似MRC中的retainCount>1的情况)。在这个例子中显然self.textField.text也是@“OneV",那么现在有两个指针指向对象@"OneV”(被持有两次,retainCount=2,其实对NSString对象说retainCount是有问题的,不过anyway~就这个意思而已.)。

过了一会儿,也许用户在textField里输入了其他的东西,那么self.textField.text指针显然现在指向了别的字符串,比如@“onevcat",但是这时候原来的对象已然是存在的,因为还有一个指针firstName持有它。现在指针的指向关系是这样的:

只有当firstName也被设定了新的值,或者是超出了作用范围的空间(比如它是局部变量但是这个方法执行完了或者它是实例变量但是这个实例被销毁了),那么此时firstName也不再持有@“OneV",此时不再有指针指向@"OneV",在ARC下这种状况发生后对象@"OneV"即被销毁,内存释放。

类似于firstNameself.textField.text这样的指针使用关键字strong进行标志,它意味着只要该指针指向某个对象,那么这个对象就不会被销毁。反过来说,ARC的一个基本规则即是,只要某个对象被任一strong指针指向,那么它将不会被销毁。如果对象没有被任何strong指针指向,那么就将被销毁。在默认情况下,所有的实例变量和局部变量都是strong类型的。可以说strong类型的指针在行为上和MRC时代retain的property是比较相似的。

既然有strong,那肯定有weak咯~weak类型的指针也可以指向对象,但是并不会持有该对象。比如:

__weak NSString *weakName = self.textField.text

得到的指向关系是:

这里声明了一个weak的指针weakName,它并不持有@“onevcat"。如果self.textField.text的内容发生改变的话,根据之前提到的"只要某个对象被任一strong指针指向,那么它将不会被销毁。如果对象没有被任何strong指针指向,那么就将被销毁”原则,此时指向@“onevcat"的指针中没有strong类型的指针,@"onevcat"将被销毁。同时,在ARC机制作用下,所有指向这个对象的weak指针将被置为nil。这个特性相当有用,相信无数的开发者都曾经被指针指向已释放对象所造成的EXCBADACCESS困扰过,使用ARC以后,不论是strong还是weak类型的指针,都不再会指向一个dealloced的对象,从根源上解决了意外释放导致的crash。

不过在大部分情况下,weak类型的指针可能并不会很常用。比较常见的用法是在两个对象间存在包含关系时:对象1有一个strong指针指向对象2,并持有它,而对象2中只有一个weak指针指回对象1,从而避免了循环持有。一个常见的例子就是oc中常见的delegate设计模式,viewController中有一个strong指针指向它所负责管理的UITableView,而UITableView中的dataSourcedelegate指针都是指向viewController的weak指针。可以说,weak指针的行为和MRC时代的assign有一些相似点,但是考虑到weak指针更聪明些(会自动指向nil),因此还是有所不同的。细节的东西我们稍后再说。

注意类似下面的代码似乎是没有什么意义的:

__weak NSString *str = [[NSString alloc] initWithFormat:…];
NSLog(@"%@",str); //输出是"(null)"

由于strweak,它不会持有alloc出来的NSString对象,因此这个对象由于没有有效的strong指针指向,所以在生成的同时就被销毁了。如果我们在Xcode中写了上面的代码,我们应该会得到一个警告,因为无论何时这种情况似乎都是不太可能出现的。你可以把weak换成strong来消除警告,或者直接前面什么都不写,因为ARC中默认的指针类型就是strong

property也可以用strongweak来标记,简单地把原来写retainassign的地方替换成strong或者weak就可以了。

@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, weak) id  delegate;

ARC可以为开发者节省很多代码,使用ARC以后再也不需要关心什么时候retain,什么时候release,但是这并不意味你可以不思考内存管理,你可能需要经常性地问自己这个问题:谁持有这个对象?

比如下面的代码,假设array是一个NSMutableArray并且里面至少有一个对象:

id obj = [array objectAtIndex:0];
[array removeObjectAtIndex:0]; 
NSLog(@"%@",obj);

在MRC时代这几行代码应该就挂掉了,因为array中0号对象被remove以后就被立即销毁了,因此obj指向了一个dealloced的对象,因此在NSLog的时候将出现EXCBADACCESS。而在ARC中由于obj是strong的,因此它持有了array中的首个对象,array不再是该对象的唯一持有者。即使我们从array中将obj移除了,它也依然被别的指针持有,因此不会被销毁。

一点提醒

ARC也有一些缺点,对于初学者来说,可能仅只能将ARC用在objective-c对象上(也即继承自NSObject的对象),但是如果涉及到较为底层的东西,比如Core Foundation中的malloc()或者free()等,ARC就鞭长莫及了,这时候还是需要自己手动进行内存管理。在之后我们会看到一些这方面的例子。另外为了确保ARC能正确的工作,有些语法规则也会因为ARC而变得稍微严格一些。

ARC确实可以在适当的地方为代码添加retain或者release,但是这并不意味着你可以完全忘记内存管理,因为你必须在合适的地方把strong指针手动设置到nil,否则app很可能会oom。简单说还是那句话,你必须时刻清醒谁持有了哪些对象,而这些持有者在什么时候应该变为指向nil

ARC必然是Objective-C以及Apple开发的趋势,今后也会有越来越多的项目采用ARC(甚至不排除MRC在未来某个版本被弃用的可能),Apple也一直鼓励开发者开始使用ARC,因为它确实可以简化代码并增强其稳定性。可以这么说,使用ARC之后,由于内存问题造成的crash基本就是过去式了(OOM除外 :P)

我们正处于由MRC向ARC转变的节点上,因此可能有时候我们需要在ARC和MRC的代码间来回切换和适配。Apple也想到了这一点,因此为开发这提供了一些ARC和非ARC代码混编的机制,这些也将在之后的例子中列出。另外ARC甚至可以用在C++的代码中,而通过遵守一些代码规则,iOS 4里也可以使用ARC(虽然我个人认为在现在iOS 6都呼之欲出的年代已经基本没有需要为iOS 4做适配的必要了)、

总之,聪明的开发者总会尝试尽可能的自动化流程,已减轻自己的工作负担,而ARC恰恰就为我们提供了这样的好处:自动帮我们完成了很多以前需要手动完成的工作,因此对我来说,转向ARC是一件不需要考虑的事情。

时间: 2014-07-16

ARC入门的相关文章

手把手教你ARC——iOS/Mac开发ARC入门和使用

本文部分实例取自iOS 5 Toturail一书中关于ARC的教程和公开内容,仅用于技术交流和讨论.请不要将本文的部分或全部内容用于商用,谢谢合作. 欢迎转载本文,但是转载请注明本文出处:http://www.onevcat.com/2012/06/arc-hand-by-hand/ 本文适合人群:对iOS开发有一定基础,熟悉iOS开发中内存管理的Reference Counting机制,对ARC机制有听闻很向往但是一直由于种种原因没有使用的童鞋.本文将从ARC机理入手对这个解放广大iOS开发者

iOS Automatic Reference Counting(ARC)

引言: ARC的全称是Automatic Reference Counting,中文翻译过来是:自动引用计数,是苹果在WWDC2011发布iOS5时随同一起的新特性.其用途是为了加强内容管理的便利性和稳定性.简而言之是为了取代MRC.并且ARC是编译时特性,它的性能和MRC不相上下,甚至效率更高,苹果建议所有的开发者都去尝试使用,提高生产效率.本文将对ARC进行一个全面的介绍. 参考资料:  1:手把手教你ARC--iOS/Mac开发ARC入门和使用 http://onevcat.com/201

WWDC 2012 Session笔记——405 Modern Objective-C

这是博主的WWDC2012笔记系列中的一篇,完整的笔记列表可以参看这里.如果您是首次来到本站,也许您会有兴趣通过RSS,或者通过页面左侧的邮件订阅的方式订阅本站. 2007年的时候,Objective-C在TIOBE编程语言排名里还排在可怜的第45位,而随着移动互联网的迅速发展和iPhone,iPad等iOS设备的广阔市场前景,Objective-C也迅速崛起,走进了开发者的视野.在最近的TIOBE排名中,Objective-C达到了惊人的第4名,可以说已经成为当今世界上一门非常重要的编程语言.

ios入门OC_UI晋级学什么?

1. OC 语法初步, 你可能学到面向对象最近本的概念, 并且可以大致的建立几个自以为是的类,但这仅仅是开始. 你知道为什么面向对象要有3大特性么.知道他们是用到什么设计模式的么 2. 你可能学到了NSString, NSMutableString 字符串的基本操作方法, 你可能会花大量的时间去看那些方法. 从没考虑过方法的实用性. UI方法成千上万, 大量的时间浪费到寻找上边可能会很累的. 所以, 学会现用现看 3. 你可能学到了NSArray, NSMutableArray, NSDicti

Swift语言入门教程

关于Swift Swift是为IOS和OSX应用制定的新编程语言,吸取C和Objective-C语言的精粹,但不损失与C语言的兼容性.Swift采用安全编程模型.加入了各种现代编程语言特性,使得该语言更易被掌握.更具扩展性,用起来更有趣.Swift语言的奠基石是已经成熟的.并为大家所喜爱的Cocoa和Cocoa Touch框架,新语言使大家可以尽情畅想新软件开发的机遇. Swift沉积了多年的研发成果,苹果公司为提供高效Swift语言编译器.调试器和基础架构打下了坚实基础.我们使用Automat

06. Web大前端时代之:HTML5+CSS3入门系列~HTML5 画布

Web大前端时代之:HTML5+CSS3入门系列:http://www.cnblogs.com/dunitian/p/5121725.html 我们先看看画布的魅力:     初始画布 canvas默认是宽300px,高150px; 绘制步骤 1.定义一个id <canvas id="canvasOne" width="300" height="300"></canvas> 2.获取canvas对象 var canvasO

《AutoCAD 2013中文版从入门到精通》——2.2 圆类命令

2.2 圆类命令 AutoCAD 2013中文版从入门到精通 圆类命令主要包括"圆"."圆弧"."圆环"."椭圆"以及"椭圆弧"命令,这几个命令是AutoCAD中最简单的曲线命令. 2.2.1 圆 执行方式 命令行:CIRCLE(快捷命令:C) 菜单栏:"绘图"→"圆" 工具栏:单击"绘图"工具栏中的"圆"按钮 操作步骤 命令行

IOS 6 自动布局 入门-1(IOS中autolayout和之前版本autoresize的差异)

来自Ray:恭喜各位!你们已经通过宣传ios feast提前解锁了第一个有关IOS6的教程. 目前这份教程只是我们的新书iOS 6 By Tutorials里面某个章节的精简版.这份教程由同样著作过iOS Apprentice Series 的Matthijs Hollemans 完成,开始体验吧! 这份教程由IOS 教程小组的组员 Matthijs Hollemans发布,Matthijs 既是一位经验丰富的IOS程序员又是一名资深老到的界面设计者. 你是否曾经因为尝试想让你的应用同时在景观方

全文检索(elasticsearch入门)

Elasticsearch篇: Elasticsearch是一个采用java语言开发的,基于Lucene构造的开源,分布式的搜索引擎. 设计用于云计算中,能够达到实时搜索,稳定可靠. Elasticsearch的数据模型是JSON. 对于需要分布式需求的这是一个非常好的选择,部署简单,同网段内会自动组成集群服务无需配置.其集成数据库同步插件,不仅支持几乎实时的全文检索服务,还支持距离查询,提供类似类似百度地图离我最近查询. 官方主页:http://www.elasticsearch.org/