内存管理篇: 5.ARC的规则补充
- ARC的方法命名
我们都知道,在内存管理的思考方式中,当方法命名以“alloc、new、copy、mutableCopy”开头或直接命名时,返回对象(必须返回对象)的内存由接收方进行管理。这种方式在ARC中同样适用。
在ARC环境下,以init或init开头的方法,也需要遵循此规则。且init的方法规定更为严格:
- 必须返回id或类型的对象
- 必须是实例方法
- OC对象(这里代指Foundation框架中类的对象)不能直接作为C数据结构的成员。
原因:ARC为OC语言编译器特性,不支持对C语言进行内存管理(最新的iOS 12中,好像已经支持此特性了,LLVM真强大。。。)
对于需要作为成员的OC对象,解决方法:
- 使用“__unsafe_unretained”进行所有权修饰,显示声明并告知编译器不要对其进行内存管理;
- 通过“__bridge”将OC对象转换为(void *)指针。
- 显示转换“id”与“void *”
对象与C指针之间是通过“__bridge”关键字进行转换,主要分为三种方式:
- __bridge
- __bridge_retained
- __bridge_transfer
- __bridge:
直接转换,忽略ARC内存管理,与添加__unsafe_unretained效果类似:
id obj = [[NSObject alloc] init];
// 转换为c指针
void *p = (__bridge void *)obj;
// 转换为OC对象
id obj2 = (__bridge id)p;
- __bridge_retained:
意为“转换后进行保留”,接收方对其自动强引用(同时掌握所有权),与__strong的retain效果类似:
void *p = 0;
{
id obj = [[NSObject alloc] init];
// 转换为c指针
p = (__bridge_retained void *)obj;
}
// 出作用域后,obj强引用消失
// 由于__bridge_retained,p同时也是强引用,对象得以继续生存
NSLog("%@", [(__bridge id)p class]);
- __bridge_transfer:
意为“转换后释放”,原创建方在转换后自动释放自身引用,只由接收方进行内存管理(转换了所有权),与release效果类似:
void *p = xxx;
id obj = (__bridge_transfer id)p;
// 转换后相当于p进行了release操作,只有obj持有对象
OC(Foundation)对象与CF(Core Foundation)对象间的转换:
- 由于二者基本是等效框架(只是实现语言不同),对象间的转换无需消耗CPU资源,是“免费桥接”(“toll-free bridge”)。
- 一般转换规则:
- OC -> CF,使用“__bridge_retained”或“CFBridgingRetain”
- CF -> OC,使用“__bridge_transfer”或“CFBridgingRelease”(因为一般转换为OC对象后,ARC可以自动介入,防止忘记使用CFRelease手动释放内存)
总结:尽量不要直接使用__bridge进行转换,使用时需再三考虑内存管理情况。
- 使用__strong、__weak和__autoreleasing修饰的变量,其初始值会自动赋值为nil