春节前抽空花了一天的时间将手头的工程从MRC转成了ARC,而后陆陆续续地修复一部分由于转ARC引发的内存泄漏和崩溃,到目前为止工程也算是比较稳定了,抽空记上一笔。(虽然说这种事情这辈子估计都只会作这么一次了,可是能够留点经验给后来的童鞋)性能
这个工程启动于12年末13年初,一开始人手少工期短,须要尽快地出demo,同时抱着对面世才一年多的ARC不太信任的态度沿用了最熟悉的MRC。可是随着工程投入的人手增多,使用MRC的各类缺点也暴露无遗:ui
1.零星的内存泄漏增多,致使每次发版本以前都要捋一遍:费时费力不讨好。虽然我一直以为iOS下这种零星的内存泄漏并非多大的事,真正压死一个App的泄漏永远是某些Bitmap的泄漏。可是这种零星泄漏仍旧会带来不少麻烦,因此仍是须要解决一下。spa
2.没法享受到ARC下weak关键字带来的好处:总有童鞋忘记在对象析构时去置空delegate。一个比较典型的例子是带Scrollview(尤为是MKMapView)的VC。若是VC析构时不置空Scrollview的delegate,那么若此时Scrollview还正处于滑动状态,就很容易出现崩溃。缘由是:Scrollview在滑动时有一个Timer retain自身,此时退出VC并不会使得Scrollview同步析构。而当Timer fire时,Scrollview调用其delegate方法就访问了野指针。指针
3.ARC下编译器对于retain cycle的检测更为严格。(我的使用后的感受)对象
4.愈来愈多的第三方库只提供ARC版本,虽然打标记能够解决问题,但增长了无谓的工做。内存
基于以上4点理由,因而选了春节前一个月高风黑夜悄悄地完成工程的ARC转换。23333333333333333编译器
准备工做同步
1.一个MRC模式的工程。(嗯!)it
2.一个合适版本的XCode。(你是鸡丁?不,我是喜儿肉丝)虽然XCode4以后就支持了ARC的自动转换,可是对ObjC++的支持却仍是在XCode5以后。编译
3.一台性能彪悍的机器。我的悲惨经历:某天下午用本身那台老爷机作了一次转换,结果在最后一步机器直接卡死,重启后XCode也没法使用,最后只得重装XCode了事。
使用XCode作最基本的转换
1.开启即便出错也继续编译的选项:"Preferences" -> "General" -> "continue building after error" 。当转换开始后工程将出现大量的错误,与其每次fix一个错误再来一遍,还不如一口气让编译器把全部的错误都先汇报出来,再一一解决。(咱们的工程大约20来万代码,检查出了近200个错误)
2.检查第三方库和本身的代码。对于第三方库,有ARC版本就进行替换,没有则打上-fno-objc-arc的标记(固然若是第三方库比较简单,也能够直接作转换)。而本身的代码则推荐所有作ARC的转换。
3.使用XCode提供的Convert to Objective-C ARC功能,选择当前须要转换的工程并执行。
4.正常状况会出现比较多的错误和retain-cycle的warning,推荐优先解决掉全部error,而warning暂时不处理,等转换完毕编译经过后再进行处理。而error和warning通常也就是下面几种状况:
a.对于NSObject和CF对象没有使用bridge cast,大多数状况下直接按照XCode的推荐方式进行fix便可。
b.原来MRC下使用了ARC下不容许使用的方法,如NSMakeCollectable。
c.原先使用__block关键字避免循环引用的地方在ARC每每会引发循环引用,缘由是__block在MRC和ARC下的语意不一样,MRC下生成的__block结构体内只是简单地指向原值地址,而ARC下则是由__block结构体持有了原值,使用__weak进行修改便可。
处理完毕后重复第三步直到顺利编译经过。
后续处理
完成前面的步骤整个转换就算完成了百分之九十,可是正所谓行百里者半九十,接下去的任务更加艰巨,更须要认真对待。
1.检查工程内的全部文件,包括是否设置了合理的编译选项和被包含在工程内:XCode彷佛有个bug,在转换完成后部分文件会被移出工程,部分文件原先打好的-fno-objc-arc标记也会被重置。
2.运行Instrument,检查内存泄漏:此时存在的内存泄漏大可能是一些对ARC不适用的MRC写法。典型的状况即是将delegate做为类内部成员变量,在转换为ARC后系XCode并不会在这些变量前面打上weak的标记,致使了循环引用,须要本身手动添加。
3.对你的程序进行冒烟,尽可能走完主流程,检查是否有必现崩溃。通常都是由错误的bridge cast和使用MRC时的不规范写法引发:如万恶的[self retain],在ARC转换时XCode直接去掉这句话,这样就致使原先依赖于此的类每每在初始化后就直接被释放,形成野指针访问。(吐槽下,不管是MRC仍是ARC下,本身去拥有本身这种作法都是不太好的作法)