App内存管理由MRC转ARC方案

App内存管理由MRC转ARC方案

一、目的

ARC(Auto Reference Counting)之前,iOS内存管理无论对于资深级开发者还是新手来说都是一件很头疼的事,在开发过程中总是避免不了写出内存泄漏的代码,开发者不得不花费大量的时间在内存管理上,即使如此依然会出现内存泄漏或者release一个已被释放的对象从而导致crash,现在Apple对iOS/Mac OS的开发引入了ARC,使用ARC开发者不再需要手动的retain release autorelease了,编译器会在合适的地方自动插入对应的代码,再结合Object-Cruntime机制实现自动引用计数.

二、ARC的适用范围

1,适用ARC的类型

  • block(ARC已经能够管理GCD对象)
  • objective对象,id, Class,NSError...等
  • 由attribute((NSObject))标记的类型

2、不适用ARC的

  • double * CFStringRef等不适用ARC,仍然需要手动管理内存 注 * 以CF开头的Core Foundation的对象往往需要手动管理内存

三、转换过程

1、代码备份

在进行大规模的更改之前,我们要进行代码备份

2、过滤代码

  • 找出项目中引用的仍然使用手动内存管理的第三方库,或者哪些你不希望转换的文件,对其添加-fno-objc-arc标记
  • Xcode自动转换工具只针对Objective-C对象,只会处理Objective-C/Objective-C++即后缀名为.m/.mm的两种文件,因此其他的C/C++对应的.c/.cpp都无需处理

3、执行检查操作

Xcode转换工具入口Edit->Convert->To Objective-C ARC,点击之后进入检查操作入口

该步骤要选择哪些文件需要转换,如果前边步骤已经把无需转换的文件都添加了-fno-objc-arc标记的话,这里可以全选。点击 check按钮后Xcode会帮助我们检查代码中存在的不符合ARC适用规则的错误或者警告,只有所有的错误都解决之后才能执行真正的转换操作。 解决完所有的error之后,点击check会弹出下面界面:

说明错误已完全修改完毕,点击next弹出下面界面,此时正在检查项目中转换成arc之后需要更改的代码:(此处会比较耗时,请耐心等待)

Xcode会弹出review界面展示,大意是Xcode将要将你的工程转换成适用ARC管理内存,所有更改的代码在真正更改之前会展示在这个review界面,同时所有的更改完成后,Xcode会将项目Target对应的工程设置(Objective-C Automatic Reference Counting)置为YES。 然后点击next按钮后会跳转到review界面,类似适用Xcode时的SVN确认提交界面,如下图

该界面列出了所有需要有更改的文件,同时能够直观的对比转换前后代码的变化,为了保险起见,建议此处每个文件都点进去扫一眼,检查是否有漏掉的不能转换的文件,确定之后点击save按钮,至此转换成功。

注: 成功之后编译有可能会报错,然后发现之前标记了-fno-objc-arc的文件,在这里所有的标记都没清了,又重新标记了一边。

四、常见错误及警告解决方案

规范

1、方法命名必须遵循命名规则,不能随意定义以alloc init new copy mutableCopy开头且和所有权操作无关的方法。**。大家可能会无意中使用newcopy开头的名字作为方法名,例如给一个换行的方法命名为newLine,给一个获取版权的方法命名为copyRight等。这些命名都是不正确的,有可能造成释放,也可能在编译时提示警告或错误。因此要切记严守内存管理相关的函数命名规则。 未使用ARC的时候你可能没有按照ARC中的命名规则来为方法起名,而这种情况下如果因为某些原因没法修改这些方法的名字,那么将这些代码迁移到ARC环境中就会有问题。这是可以通过给方法加上事先定义好的一些宏来告诉编译器应该如何对这个方法的返回值进行内存管理。 如:NS_RETURNS_RETAINED指明这个方法和initcopy开头的方法一样,由调用端负责释放返回的对象。NS_RETURNS_NOT_RETAINED指明这个方法不属于内存管理方面的方法,调用端无需释放返回的对象。 如下方法

+ (FinishingDate*)newMoon NS_RETURNS_NOT_RETAINED;
复制代码

虽然他是一个以new开头的方法,但是通过后面的宏来告诉编译器,这是方法的返回值不需要调用端释放。编译时,这些宏会被替换为注释(annotation)。这些宏被定义在了NSObjCRuntime.h中。 如:

-(MRCTest*)NewMRCTestFun NS_RETURNS_NOT_RETAINED;
复制代码

通过Preprocess后:

-(MRCTest*)NewMRCTestFun __attribute__((ns_returns_not_retained))
复制代码

也可以通过条件编译确定ARC是否有效 编译条件是:

#if __has_feature(objc_arc)
复制代码

如果为真,则表明ARC有效,否则就表示ARC无效。 2、在ARC中,如果实现了-(void)dealloc;依然会执行,但是重写dealloc时不需要调用[super dealloc]方法。 3、使用@autorelease代替NSAutoreleasePool。 4、iOS的世界中有两种对象:Objective-C对象和Core Foundation对象。其中,Core Foundation类型的对象不再ARC的管理范畴内。因此,当转换这两种类型时,就要告诉编译器怎样处理对象的所有权。基于相同的原因,在ARC程序中,就算加上__unsafe_unretained上修饰符,id类型和void*类型之间也是不能进行转换的。可以使用_bridge 修饰符来实现它们之间的相互转换。

错误

1、报错:error: ARC forbids Objective-C obj in structs or unions。 原因:ARC有效的情况下,不可以在C语言的结构体或共用体中定义Objective-C的对象。原因是编译器不能自动释放结构体(或共用体)内部的Objective-C对象。 解决方案:使用Objective-C的类来代替结构体或共用体。如果实在是因为效率或其它原因无论如何都要使用结构体的话,可以使用__unsafe_unretained修饰符来修饰结构体中的Objective-C变量。这样一来,编译器就不会管理这个变量的内存,所以需要完全手动的管理内存。 2、报错:ARC forbids synthesizing a property of an Objective-C object with unspecified ownership or storage attribute 原因:property属性必须指定一个内存管理关键字。 解决方案:在属性定义处增加strong weak copy关键字即可。 3、报错: ARC forbids explicit message send of ‘release’ 原因:通常是使用包含release的宏定义。

注:我们在之前的自动转换中无法自动转换使用了宏释放的代码
复制代码

解决方案:将该宏和使用该宏的地方删除或者将宏定义中的release去掉即可。 4、报错:Init methods must return a type related to the receiver type

@interface HomeViewController : UIViewController
- (UILabel *)initLabelWith...
@end
复制代码

原因:A类里的一个方法以init开头,而且返回的是B类型; 解决方案:修改方法名字 5、报错:Cast of C pointer type ‘ivPointer’ (aka ‘void ’) to Objective-C pointer type ‘iFlyTTSManager_old ’ requires a bridged cast cast_pointer_objective-c 原因:Toll-Free Bridging转换问题,在ARC下根据情况使用对应的转换关键字就行了 解决方案: MRC下的Toll-FreeBridging不涉及内存管理的转移,Objective-C(后文简称OC)和Core Foundation(后文简称CF)各自管理各自的内存,相互之间可以直接交换使用,比如:

NSLocale *gbNSLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"];
CFLocaleRef gbCFLocale = (CFLocaleRef)gbNSLocale;
复制代码

而在ARC下,事情就会变得复杂一些。因为ARC能够管理OC对象的内存,却不能管理CF对象,CF对象依然需要我们手动管理内存。在CF和OC之间 bridge对象的时候,问题就出现了,编译器不知道该如何处理这个同时有OC指针和CF指针指向的对象。这时候,需要使用__bridge, __bridge_retained, __bridge_transfer等修饰符来告诉编译器该如何去做。

  • __bridge

它告诉编译器仍然负责管理好在OC一端的引用计数的事情,开发者也继续负责管理好在CF一端的事情,比如:

CFStringRef cfString = CFStringCreateWithCString(kCFAllocatorDefault, "CFString", kCFStringEncodingUTF8);
NSString *ocString = (__bridge NSString *)cfString;
CFRelease(cfString);
NSLog(@"%@",ocString);
复制代码
  • __bridge_retainedCFBridgingRetain

二者作用是一样的,只是用法不同。 告诉编译器需要retain对象,而开发者在CF一端负责释放。这样就算对象在OC一端被释放,只要开发者不释放CF一端的对象, 对象就不会被真的销毁。

NSArray *ocArray = [[NSArray alloc] initWithObjects:@"foggry", nil];
CFArrayRef cfArray = (__bridge_retained CFArrayRef)ocArray;
/**
使用cfArray
**/
CFRelease(cfArray);
复制代码
  • __bridge_transferCFBridgingRelease 二者作用也是一样的,只是用法不同。 该关键字告诉编译器bridge的同时,也转移了对象的所有权,比如:
CFStringRef cfString = CFStringCreateWithCString(kCFAllocatorDefault, "CFString", kCFStringEncodingUTF8);
NSString *ocString = (__bridge_transfer NSString *)cfString;
//CFRelease(cfString); //不再需要释放操作
NSLog(@"%@",ocString);
复制代码

转换过程中大家只需要根据具体需求选用适当的关键字即可。 另外,在ARC中id和void *也不能直接相互转换了,必须通过Toll-FreeBridging使用适当的关键字修饰。

6、报错: error: ’NSAllocateObject’ is unavailable: not available in automatic reference counting mode 原因:NSAllocateObject在ARC模式下禁用 解决方案:修改创建方式

7、报错: Cannot assign to 'self' outside of a method in the init family 原因:init方法后边首个字母需要大写 Xcode判断是否为init方法规则:方法返回id,并且名字以init+大写字母开头+其他 为准则 解决方案:修改方法命名

警告

1、警告:Capturing self in this block is likely to lead to a retain cycle 原因:这是典型的block循环引用问题; 解决方案:将block中的self改成使用指向self的weak指针即可。 2、警告:Using ‘initWithArray:’ with a literal is redundant 原因:没必要的alloc操作 解决方案:直接按Xcode提示将alloc删除即可 3、警告:Property follows Cocoa naming convention for returning ‘owned’ objects 原因:@property属性的命名以关键字开头了。 解决方案:修改方法是将对应的getter方法改成非关键字开头命名的:

  • 注:ARC下方法名如果是以new/alloc/init等开头的,而且还不是类的初始化方法,就该小心了,要么报错,要么警告,原因你懂的。

4、警告:Capturing ‘self’ strongly in this block is likely to lead to a retain cycle 原因:明显的block导致循环引用内存泄露的情况 解决方案: weakSelf

Xcode 自动转换

关键字转换

  • retain自动转成strong
  • assign关键字转成weak
  • 修饰Objective-C对象或者id类型对象的assign关键字会被转成weak,但是修饰Int/bool等数值型变量的assign不会自动转换成weak

关键字删除

  • 和手动内存管理相关的几个关键字,比如:release/retain/autorelease/super dealloc等会被删除;
  • dealloc方法中如果除了release/super dealloc语句外,如果别的代码,dealloc方法会保留,如果没有整个方法都会被删除

关键字替换

  • 在转换时block关键字会被自动替换成weak;
  • NSAutoreleasePool不支持ARC,会被替换成@autoreleasepool;

帮助: 手动内存管理(MRC)转ARC

王保湘 2018年4月2日

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值