Convert to Objective-C ARC

今天在进行代码走查时,竟然发现了下面这段代码:

1
2
3
for (int i = 0; i < someObj.retainCount; i++) {
[someObj release];
}

顿时感觉吐槽无力,虽然我反复强调内存管理问题,无非就是谁申请谁释放,利用强弱引用避免retain-cycles,但是还是会有这样那样的问题,leaks每次就是一片红。本来是计划等他们交易都开发完了,进行一次集体代码走查,好好给他们上一课,集中来解决内存问题。但是由于个人原因我7月份会离开项目组,恐怕没有时间来这么做了,所以最终还是决定将工程转成ARC模式。

该项目是某行手机银行客户端,iOS开发这块除了我,其他的所有7个开发人员都是项目组临时招聘的,技术参差不齐,毕竟公司招聘标准就是:便宜 + 能干事。我的职责就是负责客户端架构,公共机制的设计与实现,公共组件的封装,开发过程中的解疑。其他开发人员每人负责一两个模块的交易开发,其实无非就剩下请求数据绘界面的事了。

其实项目开始时就打算尝试用ARC的,但是项目组内其他人员之前都没接触过ARC,迫于项目进度压力也没有时间做培训,就使用大家比较熟悉的MRR,还是太信任他们了。

Convertion Steps

1.将不要转成ARC的代码标注一下

如开源代码AFNetworking(因为项目需要支持4.3,所以用的AFNetworking是支持4.3的非ARC版本)、GDataXML、RegexKitLite、Reachability等,其实这些也能转,但是感觉没有必要,将这些源文件在Build Phases / Complie Sources中标注为-fno-objc-arc,如果之前用到了使用ARC的代码,现在可以将-fobjc-arc标记去掉了。

setp1

2.工程检查

我选择使用Xcode5.0_DP版本来转,之前使用Xcode4.6在转换过程中电脑经常会假死,或者Xcode4.6直接崩溃,不知道什么原因。而Xcode5.0默认就是ARC模式,我想为了能让之前的程序都转换为ARC,它的转换工具一定比之前强化了不少。

Xcode -> Edit -> Refactor -> Convert to Objective-C ARC…

选择要转换的Target,工程target,如果有单元测试也会出现单元测试的target,点击“Check”。

3.解决ARC不允许的问题

setp3

转换工具会自动将代码里面的retain、release、autorelease等操作去掉,属性中的retain、copy、assign转为为对应的关键字(retain、copy -> strong, assign会根据工程是否支持5.0一下版本转出weak,或unsafe_unretained)。

但是如果你在宏里面定义如:

#define MB_RELEASE_SAFELY(__POINTER) { [__POINTER release]; __POINTER = nil; }

转换工具是不会自动处理里面的release的,需要手动将[__POINTER release];去掉。(我工程里面4201个问题基本上都是因为这个导致的)

此外在CF对象与NS对象之间转换的需要加上关键字__bridge,这个也需要手动来来修改。

4.开始转换

setp4

解决完所有的冲突,再次选择“Convert to Objective-C ARC…”,你就能看到这个界面,点击“Next”后,再次经过漫长的等待, 工具会列出了将会为了转换的代码的对照列表。默认所有出现在列表里面的文件都是选中的,你可以选在不要转换的文件,去掉对勾。确认完后,点击“Save”就完成了整个工程的转换工作, Congratulations!

(我在完成后编译时,发现之前被我标为-fno-objc-arc的文件会有报错,原来在转换过程中工具将之前的标记都去掉了,无奈再次标记了一遍。)

5.其他处理

因为我们工程支持4.3,所以weak, __weak关键字不能用, 可以通过宏定义将5.0以下时weak关键字定义为unsafe_unretained:

1
2
3
4
5
6
7
8
9
10
#if (!__has_feature(objc_arc)) || \
(defined __IPHONE_OS_VERSION_MIN_REQUIRED && \
__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0) || \
(defined __MAC_OS_X_VERSION_MIN_REQUIRED && \
__MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_7)
#undef weak
#define weak unsafe_unretained
#undef __weak
#define __weak __unsafe_unretained
#endif

在ARC模式下,使用performSelector方法会有编译警告:PerformSelector may cause a leak because its selector is unknown,作为有洁癖的程序员是不能容许程序中有警告的:

1
2
3
4
#progma clang diagnostic push
#progma clang disgnostic ignored "-Warc-performSelecotr-leaks"
[self performSelector:@selector(mySelector:) withObject:object];
#progma clang disgnostic pop