C 指针与 OC 对象之间的转换
Core Foundation 框架
Core Foundation 框架 (CoreFoundation.framework) 是一组 C 语言接口, 简称 CF.
它们为 iOS 应用程序提供基本数据管理和服务功能.
如 Core Graphics、Core Text,并且我们可能需要将 CF 对象和OC 对象进行相互转化,ARC 下,编译器不会自动管理 CF 对象的内存,我们需要手动管理.
创建一个 CF 对象使用后, 需要使用 CFRelease 将其手动释放, 换句话说, Core Foundation 对象类型不在 ARC 管理范畴内.
如何将 CF 和 OC 对象有效的结合起来, 在 ARC 环境下, 提供了 桥接
的技术, 即 ARC 下 OC 对象和 Core Foundation 对象之间的桥梁.
ARC 桥接
ARC 下 C 指针与 OC 指针(对象)之间转换, 一般会用到下面的方法.
1 | __bridge_retained <#CF type#>)< |
也就是所谓的 桥接
, 它是 Object-C 在 ARC 环境下开发出来的一种用作转换 C 指针跟 OC (类)指针的一种转换技术, 所以是 ARC 下的称谓, 在 MRC 下没有 桥接
.
针对内存管理问题,ARC 可以管理 Objective-C 对象, 但不支持 Core Foundation 对象的管理,所以转换后要注意一个问题:谁来释放使用后的对象.
结合 ARC 和 内存管理, 下面分别介绍一下.
Core Foundation 对象必须使用 CFRetain 和 CFRelease 来进行内存管理.
当使用 Objective-C 和 Core Foundation 对象相互转换的时候,必须让编译器知道,到底由谁来负责释放对象,是否交给 ARC 处理, 只有正确的处理,才能避免内存泄漏和 double free 导致程序崩溃.
__bridge_retained <#CF type#>)<#expression#>
__bridge_retained
等同于 CFBridgingRetain()
.
将 Objective-C 对象转换为 Core Foundation 对象,把对象所有权桥接给 Core Foundation 对象,同时剥夺 ARC 的管理权,后续需要开发者使用 CFRelease 或者相关方法手动来释放 CF 对象.
示例:
1 | void *cPointer; |
在 ARC 下, CFBridgingRetain 实现如下:
1 | NS_INLINE CF_RETURNS_RETAINED CFTypeRef _Nullable CFBridgingRetain(id _Nullable X) { |
关于 CFTypeRef, 如下:
1 | typedef const CF_BRIDGED_TYPE(id) void * CFTypeRef; |
所以 CFBridgingRetain 返回值是 const void *
类型的.
上面的示例可以改写为:
1 | const void *cPointer; |
__bridge_transfer <#Objective-C type#>)<#expression#>
__bridge_transfer
等同于 CFBridgingRelease()
.
将非 OC 对象转换为 OC 对象,同时将对象的管理权交给 ARC,开发者无需手动管理内存.
示例:
1 | CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); |
CFBridgingRelease 实现如下:
1 | NS_INLINE id _Nullable CFBridgingRelease(CFTypeRef CF_CONSUMED _Nullable X) { |
上面的示例可以改写为:
1 | CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); |
__bridge
__bridge
不改变对象所有权, 需要我们自己来管理内存, 它也是我们经常使用的方法, 从某种程度上来说, 它是上面两个方法的简化版本.
__bridge
可以将 OC 对象 与 C 指针相互转换, 示例:
1 | //CFString -> OC 对象 |
如果将 CFRelease(cfString)
注释掉, Xcode 的静态检测器会告诉你有内存泄露的情况, 如图:
再来另外一个例子, 如下:
1 | //OC 对象 -> CFString |
无论是使用 CFRelease(cfStringRef)
, 还是注释掉 CFRelease(cfStringRef)
, 静态检测器都不会报错. 说明这种情况下, 当前的内存管理已经被 OC 对象管理.
经典例子
NSString 与 CFString 转换
1、NSString → CFString
1 | { |
2、CFString → NSString
1 | CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorDefault, "veryitman", kCFStringEncodingUnicode); |
野指针
运行下面的示例:
1 | void *p; |
会直接 crash, 如图:
当 objc
这个对象超出作用域范围,其内存就会被回收,接着在作用域范围外用 void *p
去访问 objc
的内存,就造成了野指针.
结合上面所说的, 我们可以让指针 p 对 objc 进行引用即 retain 操作, 修改如下:
1 | void *p; |
可以正常的运行. 还可以修改为另一种方式:
1 | void *p; |
扫码关注,你我就各多一个朋友~