本文引自:http://www.onevcat.com/2012/06/arc-hand-by-hand/html
英文原版:http://www.raywenderlich.com/5677/beginning-arc-in-ios-5-part-1ios
感谢以上做者对广大iOS开发者所作的贡献。git
本文适合人群:对iOS开发有必定基础,熟悉iOS开发中内存管理的Reference Counting机制,对ARC机制有听闻很向往可是一直因为种种缘由没有使用的童鞋。本文将从ARC机理入手对这个解放广大iOS开发者的伟大机制进行一个剖析,并逐步引导你开始使用ARC。一旦习惯ARC,你必定会被它的简洁高效所征服。github
虽然距离WWDC2011和iOS 5已经快一年时间,可是不少开发者并无利用新方法来提升本身的水平,这点在ARC的使用上很是明显(特别是国内,基本不多见到同行转向ARC)。我曾经询问过一些同行为何不转向使用ARC,不少人的回答是担忧内存管理不受本身控制..其实我我的认为这是对于ARC机制了解不足从而不自信,所致使的对新事物的恐惧。而做为最须要“追赶时髦”的职业,这样的心态将至关不利。谨以此文但愿能清楚表述ARC的机理和用法,也但愿可以成为如今中文入门教学缺失的补充。web
Automatic Reference Counting,自动引用计数,即ARC,能够说是WWDC2011和iOS5所引入的最大的变革和最激动人心的变化。ARC是新的LLVM 3.0编译器的一项特性,使用ARC,能够说一举解决了广大iOS开发者所憎恨的手动内存管理的麻烦。objective-c
在工程中使用ARC很是简单:只须要像往常那样编写代码,只不过永远不写retain
,release
和autorelease
三个关键字就好~这是ARC的基本原则。当ARC开启时,编译器将自动在代码合适的地方插入retain
, release
和autorelease
,而做为开发者,彻底不须要担忧编译器会作错(除非开发者本身错用ARC了)。好了,ARC至关简单吧~到此为止,本教程结束。设计模式
等等…也许还有其余问题,最严重的问题是“我怎么肯定让ARC来管理不会出问题?”或者“用ARC会让程序性能降低吧”。对于ARC不能正处理内存管理的质疑自从ARC出生以来就一直存在,而如今愈来愈多的代码转向ARC并取得了很好的效果,这证实了ARC是一套有效的简化开发复杂程度的机制,另外经过研究ARC的原理,能够知道使用ARC甚至能提升程序的效率。在接下来将详细解释ARC的运行机理而且提供了一个step-by-step的教程,将非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所作的只不过是在代码编译时为你自动在合适的位置插入release
或autorelease
,就如同以前MRC时你所作的那样。所以,至少在效率上ARC机制是不会比MRC弱的,而由于能够在最合适的地方完成引用计数的维护,以及部分优化,使用ARC甚至能比MRC取得更高的运行效率。
学习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"即被销毁,内存释放。
相似于firstName
和self.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中的dataSource
和delegate
指针都是指向viewController的weak
指针。能够说,weak
指针的行为和MRC时代的assign
有一些类似点,可是考虑到weak
指针更聪明些(会自动指向nil),所以仍是有所不一样的。细节的东西咱们稍后再说。
注意相似下面的代码彷佛是没有什么意义的:
__weak NSString *str = [[NSString alloc] initWithFormat:…]; NSLog(@"%@",str); //输出是"(null)"
因为str
是weak
,它不会持有alloc出来的NSString
对象,所以这个对象因为没有有效的strong
指针指向,因此在生成的同时就被销毁了。若是咱们在Xcode中写了上面的代码,咱们应该会获得一个警告,由于不管什么时候这种状况彷佛都是不太可能出现的。你能够把weak换成strong来消除警告,或者直接前面什么都不写,由于ARC中默认的指针类型就是strong
。
property也能够用strong
或weak
来标记,简单地把原来写retain
和assign
的地方替换成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是一件不须要考虑的事情。
说了这么多,终于能够实践一下了。在决定使用ARC后,不少开发者面临的首要问题是不知如何下手。由于可能手上的项目已经用MRC写了一部分,不想麻烦作转变;或者由于新项目里用ARC时遇到了奇怪的问题,从而放弃ARC退回MRC。这都是常见的问题,而在下面,将经过一个demo引导你们完全转向ARC的世界。
例子很简单,这是一个查找歌手的应用,包含一个简单的UITableView和一个搜索框,当用户在搜索框搜索时,调用MusicBrainz的API完成名字搜索和匹配。MusicBrainz是一个开放的音乐信息平台,它提供了一个免费的XML网页服务,若是对MusicBrainz比较有兴趣的话,能够到它的官网逛一逛。
AppDelegate.h/m 这是整个app的delegate,没什么特殊的,每一个iOS/Mac程序在main函数之后的入口,由此进入app的生命周期。在这里加载了最初的viewController并将其放到Window中展现出来。另外appDelegate还负责处理程序开始退出等系统委托的事件
MainViewController.h/m/xib 这个demo最主要的ViewController,含有一个TableView和一个搜索条。 SoundEffect.h/m 简单的播放声音的类,在MusicBrainz搜索完毕时播放一个音效。 main.m 程序入口,全部c程序都从main函数开始执行
AFHTTPRequestOperation.h/m 这是有名的网络框架AFNetworking的一部分,用来帮助等简单地处理web服务请求。这里只包含了这一个类而没有将所有的AFNetworking包括进来,由于咱们只用了这一个类。完整的框架代码能够在github的相关页面上找到https://github.com/gowalla/AFNetworking
SVProgresHUD.h/m/bundle 是一个经常使用的进度条指示,当搜索的时候出现以提示用户正在搜索请稍后。bundle是资源包,里面包含了几张该类用到的图片,打进bundle包的目的一方面是为了资源容易管理,另外一方面也是主要方面时为了避免和其余资源发生冲突(Xcode中资源名字是资源的惟一标识,同名字的资源只能出现一次,而放到bundle包里能够避免这个潜在的问题)。SVProgresHUD能够在这里找到https://github.com/samvermette/SVProgressHUD
快速过一遍这个应用吧:MainViewController
是UIViewController
的子类,对应的xib文件定义了对应的UITableView
和UISearchBar
。TableView中
显示searchResult
数组中的内容。当用户搜索时,用AFHTTPRequestOperation发一个HTTP请求,当从MusicBrainz获得回应后将结果放入searchResult
数组中并用tableView
显示,当返回结果是空时在tableView
中显示没找到。主要的逻辑都在MainViewController.m中的-searchBarSearchButtonClicked:
方法中,生成了用于查询的URL,根据MusicBrainz的需求替换了请求的header,而且完成了返回逻辑,而后在主线程中刷新UI。整个程序仍是比较简单的~
回到正题,咱们讨论的是ARC,关于REST API和XML解析的技术细节就暂时先忽略吧..整个程序都是用MRC来进行内存管理的,首先来让咱们把这个demo转成ARC吧。基本上转换为ARC意味着把全部的retain
,release
和autorelease
关键字去掉,在以前咱们明确几件事情:
对于咱们的demo,为了说明问题,这三种策略咱们都将采用,注意这仅仅只是为了展现如何转换。实际操做中不须要这么麻烦,并且从此的绝大部分状况应该是从工程创建开始就是ARC的。
首先,ARC是LLVM3.0编译器的特性,而老的工程特别是Xcode3时代的工程的默认编译器极可能是GCC或者LLVM-GCC,所以第一步就是确认编译器是否正确。在Project设置面板,选择target,在Build Settings中将Compiler for C/C++/Objective-C选为Apple LLVM compiler 3.0或以上。为了确保以后转换的顺利,在这里我我的建议最好把Treat Warnings as Errors和 Run Static Analyzer都打开,确保在改变编译器后代码依旧没有警告或者内存问题(虽然静态分析可能不太能保证这一点,可是聊胜于无)。好了~clean(Shift+Cmd+K
)之后Bulid一下试试看,通过修改后的demo工程没有任何警告和错误,这是很好的开始。(对于存在警告的代码,这里是很好的修复的时机..请在转换前确保原来的代码没有内存问题)。
接下来就是完成从MRC到ARC的伟大转换了。仍是在Build Settings页面,把Objective-C Automatic Reference Counting改为YES(若是找不到的话请看一看搜索栏前面的小标签是否是调成All了..这个选项在Basic里是不出现的),这样咱们的工程就将在全部源代码中启用ARC了。而后…试着编译一下看看,嗯..无数的错误。
这是很正常的,由于ARC里不容许出现retain,release之类的,而MRC的代码这些是确定会有的东西。咱们能够手动一个一个对应地去修复这些错误,可是这很麻烦。Xcode为咱们提供了一个自动转换工具,能够帮助重写源代码,简单来讲就是去掉多余的语句而且重写一些property关键字。
这个小工具是Edit->Refactor下的Convert to Objective-C ARC,点击后会让咱们选择要转换哪几个文件,在这里为了说明除了自动转换外的方法,咱们不所有转换,而只是选取其中几个转换(MainViewController.m
和AFHTTPRequestOperation.m
不作转换,以后咱们再手动将这两个转为ARC)。注意到这个对话框上有个警告标志告诉咱们target已是ARC了,这是因为以前咱们在Build Settings里已经设置了启用ARC,其实直接在这里作转换后Xcode会自动帮咱们开启ARC。点击检查后,Xcode告诉咱们一个不幸的消息,不能转换,须要修复ARC readiness issues..后面还告诉咱们要看到全部的所谓的ARC readiness issues,能够到设置的General里把Continue building after errors勾上…What the f**k…好吧~先乖乖遵从Xcode的建议"Cmd+,“而后Continue building after errors打勾而后再build。
问题依旧,不过在issue面板里应该能够看到全部出问题的代码了。在咱们的例子里,问题出在SoundEffect.m里:
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:filename withExtension:nil]; if (fileURL != nil) { SystemSoundID theSoundID; OSStatus error = AudioServicesCreateSystemSoundID((CFURLRef)fileURL, &theSoundID); if (error == kAudioServicesNoError) { soundID = theSoundID; } }
这里代码尝试把一个NSURL
指针强制转换为一个CFURLRef
指针。这里涉及到一些Core Services特别是Core Foundation(CF)的东西,AudioServicesCreateSystemSoundID()函数接受CFURLRef为参数,这是一个CF的概念,可是咱们在较高的抽象层级上所创建的是NSURL
对象。在Cocoa框架中,有不少顶层对象对底层的抽象,而在使用中咱们每每能够不加区别地对这两种对象进行一样的对待,这类对象即为能够"自由桥接"的对象(toll-free bridged)。NSURL和CFURLRef就是一对好基友好例子,在这里其实CFURLRef
和NSURL
是能够进行替换的。
一般来讲为了代码在底层级上的正确,在iOS开发中对基于C的API的调用所传入的参数通常都是CF对象,而Objective-C的API调用都是传入NSObject对象。所以在采用自由桥接来调用C API的时候就须要进行转换。可是在使用ARC编译的时候,由于内存管理的缘由,编译器须要知道对这些桥接对象要实行什么样的操做。若是一个NSURL对象替代了CFURLRef,那么在做用区域外,应该由谁来决定内存释放和对象销毁呢?为了解决这个问题,引入了bridge,bridgetransfer和bridgeretained三个关键字。关于选取哪一个关键字作转换,须要由实际的代码行为来决定。若是对于自由桥接机制感兴趣,你们能够本身找找的相关内容,好比适用类型、内部机制和一个简介~以后我也会对这个问题作进一步说明
回到demo,咱们如今在上面的代码中(CFURLRef)前加上__bridge
进行转换。而后再运行ARC转换工具,这时候检查应该没有其余问题了,那么让咱们进行转换吧~固然在真正转换以前会有一个预览界面,在这里咱们最好检查一下转换是否是都按照预想进行了..要是出现大面积错误又没有备份或者出现各类意外的话就能够哭了…
先后变化的话比较简单,基本就是去掉不须要的代码和改变property的类型而已,其实有信心的话不太须要每次都看,可是若是是第一次执行ARC转换的操做的话,我仍是建议稍微看一下变化,这样能对ARC有个直观上的了解。检查一遍,应该没什么问题了..须要注意的是main.m里关于autoreleasepool的变化以及全部dealloc调用里的[super dealloc]的删除,它们一样是MRC到ARC的主要变化..
好了~转换完成之后咱们再build看看..应该会有一些警告。对于原来retain
的property,比较保险的作法是转为strong
,在LLVM3.0中自动转换是这样作的,可是在3.1中property默认并非strong
,这样在使用property赋值时存在警告,咱们在property声明里加上strong
就行了~而后就是SVProgressHUD.m里可能存在问题,这是因为原做者把release
的代码和其余代码写在一行了.致使自动转换时只删掉了部分,而留下了部分不该该存在的代码,删掉对变量的空的调用就行了..
而后再编译,没有任何错误和警告了,好棒~等等…咱们刚才没有对MainViewController和AFHTTPRequestOperation进行处理吧,那么这两个文件里应该还存在release
之类的东西吧..?看一看这两个文件,果真有各类release
,可是为何能编译经过呢?!明明刚才在自动转换前他们还有N多错的嘛…答案很简单,在自动转换的时候由于咱们没有勾选这两个文件,所以编译器在自动转换事后为这两个文件标记了"不使用ARC编译"。能够看到在target的Building Phases下,MainViewController.m和AFHTTPRequestOperation.m两个文件后面被加上了-fno-objc-arc
的编译标记,被加上该标记的文件将不使用ARC规则进行编译。(相对地,若是你想强制对某几个文件启用ARC的话,能够为其加上-fobjc-arc
标记)
提供这样的编译标记的缘由是显而易见的,由于老是有一部分的第三方代码并无转换为ARC(多是因为维护者犯懒或者已经终止维护),因此对于这部分代码,为了迅速完成转换,最好是使用-fno-objc-arc标记来禁止在这些源码上使用ARC。
为了方便查找,再此列出一些在转换时可能出现的问题,固然在咱们使用ARC时也须要注意避免代码中出现这些问题:
“Cast … requires a bridged cast”
这是咱们在demo中遇到的问题,再也不赘述
Receiver type ‘X’ for instance message is a forward declaration
这每每是引用的问题。ARC要求完整的前向引用,也就是说在MRC时代可能只须要在.h中申明@class就能够,可是在ARC中若是调用某个子类中未覆盖的父类中的方法的话,必须对父类.h引用,不然没法编译。
Switch case is in protected scope
如今switch语句必须加上{}了,ARC须要知道局部变量的做用域,加上{}后switch语法更加严格,不然遇到没有break的分支的话内存管理会出现问题。
A name is referenced outside the NSAutoreleasePool scope that it was declared in...
这是因为写了本身的autoreleasepool,而在转换时在原来的pool中申明的变量在新的@autoreleasepool中做用域将被局限。解决方法是把变量申明拿到pool的申请以前。
ARC forbids Objective-C objects in structs or unions
能够说ARC所引入的最严格的限制是不能在C结构体中放OC对象了..所以相似下面这样的代码是不可用的
typedef struct { UIImage *selectedImage; UIImage *disabledImage; } ButtonImages;
这个问题只有乖乖想办法了..改变原来的结构什么的..
刚才作了对demo的大部分转换,还剩下了MainViewController和AFHTTPRequestOperation是MRC。可是因为使用了-fno-objc-arc
,所以如今编译和运行都没有问题了。下面咱们看看如何手动把MainViewController转为ARC,这也有助于进一步理解ARC的规则。
首先,咱们须要转变一下观念…对于MainViewController.h,在.h中申明了两个实例变量:
@interface MainViewController : UIViewController { NSOperationQueue *queue; NSMutableString *currentStringValue; }
咱们不妨仔细考虑一下,为何在interface里出现了实例变量的申明?一般来讲,实例变量只是在类的实例中被使用,而你所写的类的使用者并无太多必要了解你的类中有哪些实例变量。而对于绝大部分的实例变量,应该都是protected
或者private
的,对它们的操做只应该用setter
和getter
,而这正是property所要作的工做。能够说,将实例变量写在头文件中是一种遗留的陋习。更好的写实例变量名字的地方应当与类实现关系更为密切,为了隐藏细节,咱们应该考虑将它们写在@implementation里。好消息是,在LLVM3.0中,不管是否开启ARC,编译器是支持将实例变量写到实现文件中的。甚至若是没有特殊须要又用了property,咱们都不该该写无心义的实例变量申明,由于在@synthesize中进行绑定时,咱们就能够设置变量名字了,这样写的话可让代码更加简洁。
在这里咱们对着两个实例变量不须要property(外部成员不该当能访问到它们),所以咱们把申明移到.m里中。修改后的.h是这样的,十分简洁一看就懂~
#import @interface MainViewController : UIViewController @property (nonatomic, retain) IBOutlet UITableView *tableView; @property (nonatomic, retain) IBOutlet UISearchBar *searchBar; @end
而后.m的开头变成这样:
@implementation MainViewController { NSOperationQueue *queue; NSMutableString *currentStringValue; }
这样的写法让代码至关灵活,并且不得不认可.m确实是这些实例变量的应该在的地方…build一下,没问题..固然对于SoundEffect类也能够作类似的操做,这会让使用你的类的人很开心,由于.h越简单越好..P.S.另一个好处能够减小.h里的引用,减小编译时间(虽然不明显=。=)
而后就能够在MainViewController里启用ARC了,方法很简单,删掉Build Phases里相关文件的-fno-objc-arc标记就能够了~而后..而后固然是一大堆错误啦。咱们来手动一个个改吧,虽然谈不上乐趣,可是成功之后也会颇有成就~(若是你不幸在启用ARC后build仍是成功了,恭喜你遇到了Xcode的bug,请Cmd+Q而后从新打开Xcode把=_=)
红色最密集的地方是dealloc
,由于每一行都是release
。因为在这里dealloc
并无作除了release
和super dealloc
以外的任何事情,所以简单地把整个方法删掉就行了。固然,在对象被销毁时,dealloc
仍是会被调用的,所以咱们在须要对非ARC管理的内存进行管理和必要的逻辑操做的时候,仍是应该保留dealloc
的,固然这涉及到CF以及如下层的东西:好比对于retain
的CF对象要CFRelease()
,对于malloc()
到堆上的东西要free()
掉,对于添加的observer
能够在这里remove,schedule的timer在这里invalidate
等等~[super dealloc]
这个消息也再也不须要发了,ARC会自动帮你搞定。
另外,在MRC时代一个常作的事情是在dealloc
里把指向本身的delegate设成nil(不然就等着EXCBADACCESS吧 :P),而如今通常delegate都是weak
的,所以在self被销毁后这个指针自动被置成nil
了,你不用再为之担忧,好棒啊..
这个很直接,没有任何问题。去掉就好了~再也不多说
在MainViewController.m里的类扩展中定义了两个property:
@interface MainViewController () @property (nonatomic, retain) NSMutableArray *searchResults; @property (nonatomic, retain) SoundEffect *soundEffect; @end
申明的类型是retain
,关于retain
,assign
和copy
的讨论已经烂大街了,在此再也不讨论。在MRC的年代使用property能够帮助咱们使用dot notation的时候简化对象的retain
和copy
,而在ARC时代,这就显得比较多余了。在我看来,使用property和点方法来调用setter和getter是没必要要的。property只在将须要的数据在.h中暴露给其余类时才须要,而在本类中,只须要用实例变量就能够。(更新,如今笔者在这点上已经不纠结了,随意就好,本身明白就行。可是也许仍是用点方法会好一些,至少能够分清楚究竟是操做了实例变量仍是调用了setter和getter)。所以咱们能够移去searchResults和soundEffect的@property和@synthesize,并将起移到实例变量申明中:
#import "plementation MainViewController { NSOperationQueue *queue; NSMutableString *currentStringValue; NSMutableArray *searchResults; SoundEffect *soundEffect; }
相应地,咱们须要将对应的self.searchResult
和self.soundEffect
的self.都去去掉。在这里须要注意的是,虽然咱们去掉了soundEffect的property和synthesize,可是咱们依然有一个lazy loading的方法-(SoundEffect *)soundEffect
,神奇之处在于(可能你之前也不知道),点方法并不须要@property关键字的支持,虽然大部分时间是这么用的..(property只是对setter或者getter的申明,而点方法是对其的调用,在这个例子的实现中咱们事实上实现了-soundEffect这个getter方法,因此点方法在等号右边的getter调用是没有问题的)。为了不误解,建议把self.soundEffect的getter调用改写成[self soundEffect]。
而后咱们看看.h里的property~里面有两个retain
的IBOutlet。retain
关键字在ARC中是依旧可用的,它在ARC中所扮演的角色和strong
彻底同样。为了不迷惑,最好在须要的时候将其写为strong,那样更符合ARC的规则。对于这两个property,咱们将其申明为weak
(事实上,若是没有特别意外,除了最顶层的IBOutlet意外,本身写的outlet都应该是weak
)。经过加载xib获得的用户界面,在其从xib文件加载时,就已是view hierarchy的一部分了,而view hierarchy中的指向都是strong的。所以outlet所指向的UI对象不该当再被hold一次了。将这些outlet写为weak的最显而易见的好处是你就不用再viewDidUnload方法中再将这些outlet设为nil了(不然就算view被摧毁了,可是因为这些UI对象还在被outlet指针指向而没法释放,代码简洁了不少啊..)。
在咱们的demo中将IBOutlet的property改成weak
而且删掉viewDidUnload中关于这两个IBOutlet的内容~
总结一下新加入的property的关键字类型:
特别地,对于NSString
对象,在MRC时代不少人喜欢用copy,而ARC时代通常喜欢用strong…(我也不懂为何..求指教)
MainViewController如今剩下的问题都是桥接转换问题了~有关桥接的部分有三处:
编译器对前两个进行了报错,最后一个是常量转换不涉及内存管理。
关于toll-free bridged,若是不进行细究,NSString
和CFStringRef
是同样的东西,新建一个CFStringRef
能够这么作:
CFStringRef s1 = [[NSString alloc] initWithFormat:@"Hello, %@!",name];
而后,这里alloc
了而s1是一个CF指针,要释放的话,须要这样:
CFRelease(s1);
类似地能够用CFStringRef
来转成一个NSString
对象(MRC):
CFStringRef s2 = CFStringCreateWithCString(kCFAllocatorDefault,bytes, kCFStringEncodingMacRoman); NSString *s3 = (NSString *)s2; // release the object when you're done [s3 release];
在ARC中,编译器须要知道这些指针应该由谁来负责释放,若是把一个NSObject
看作是CF对象的话,那么ARC就再也不负责它的释放工做(记住ARC是only for NSObject的)。对于不须要改变持有者的对象,直接用简单的bridge就能够了,好比以前在SoundEffect.m作的转换。在这里对于(CFStringRef)text这个转换,ARC已经负责了text这个NSObject的内存管理,所以这里咱们须要一个简单的bridge。而对于CFURLCreateStringByAddingPercentEscapes
方法,方法中的create暗示了这个方法将造成一个新的对象,若是咱们不须要NSString
转换,那么为了不内存的问题,咱们须要使用CFRelease
来释放它。而这里咱们须要一个NSString
,所以咱们须要告诉编译器接手它的内存管理工做。这里咱们使用bridge_transfer关键字,将内存管理权由CF object移交给NSObject(或者说ARC)。若是这里咱们只用bridge的话,内存管理的负责人没有改变,那么这里就会出现一个内存泄露。另外有时候会看到CFBridgingRelease()
,这其实就是transfer cast的内联写法..是同样的东西。总之,须要记住的原则是,当在涉及CF层的东西时,若是函数名中有含有Create, Copy, 或者Retain之一,就表示返回的对象的retainCount+1了,对于这样的对象,最安全的作法是将其放在CFBridgingRelease()
里,来平衡retain
和release
。
还有一种bridge方式,__bridge_retained
。顾名思义,这种转换将在转换时将retainCount加1。和CFBridgingRelease()
类似,也有一个内联方法CFBridgingRetain()
来负责和CFRelease()
进行平衡。
须要注意的是,并不是全部的CF对象都是自由桥接的,好比Core Graphics中的全部对象都不是自由桥接的(如CGImage
和UIImage
,CGColor
和UIColor
)。另外也不是只有自由桥接对象才能用bridge来桥接,一个很好的特例是void _(指向任意对象的指针,相似id),对于void _和任意对象的转换,通常使用_bridge
。(这在将ARC运用在Cocos2D中颇有用)
至此整个工程都ARC了~对于AFHTTPRequestOperation这样的不支持ARC的第三方代码,咱们的选择通常都是就不使用ARC了(或者等开源社区的大大们更新ARC适配版本)。能够预见,在近期会有愈来愈多的代码转向ARC,可是也必定会有大量的代码暂时或者永远保持MRC等个,因此对于这些代码就不用太纠结了~
写了那么多,但愿你如今能对ARC有个比较全面的了解和认识了。ARC确定是之后的趋势,也确实能让代码量大大下降,减小了不少无心义的重复工做,还提升了app的稳定性。可是凡事仍是纸上得来终觉浅,但愿做为开发者的你,在下一个工程中去尝试用用ARC~相信你会和我同样,立刻爱上这种make life easier的方式的~