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 指针(对象)之间转换, 一般会用到下面的方法.
__bridge_retained <#CF type#>)<#expression#> __bridge_transfer <#Objective-C type#>)<#expression#> __bridge <#type#>)<#expression#>
也就是所谓的 桥接, 它是 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 对象.
示例:
void *cPointer; NSObject *objc = [[NSObject alloc] init]; //将 OC 对象转换为 C 指针 cPointer = (__bridge_retained void*)objc; //use cPointer ... //需要释放资源 CFRelease(cPointer);
在 ARC 下, CFBridgingRetain 实现如下:
NS_INLINE CF_RETURNS_RETAINED CFTypeRef _Nullable CFBridgingRetain(id _Nullable X) { return (__bridge_retained CFTypeRef)X; }
关于 CFTypeRef, 如下:
typedef const CF_BRIDGED_TYPE(id) void * CFTypeRef;
所以 CFBridgingRetain 返回值是 const void * 类型的.
上面的示例可以改写为:
const void *cPointer; const NSObject *objc = [[NSObject alloc] init]; cPointer = CFBridgingRetain(objc); //use cPointer ... CFRelease(cPointer);
__bridge_transfer <#Objective-C type#>)<#expression#>
__bridge_transfer 等同于 CFBridgingRelease()
.
将非 OC 对象转换为 OC 对象,同时将对象的管理权交给 ARC,开发者无需手动管理内存.
示例:
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); CFStringRef strUUID = CFUUIDCreateString(kCFAllocatorDefault, uuid); NSString *str = (__bridge_transfer NSString *)strUUID; //无需释放 strUUID //CFRelease(strUUID); CFRelease(uuid);
CFBridgingRelease 实现如下:
NS_INLINE id _Nullable CFBridgingRelease(CFTypeRef CF_CONSUMED _Nullable X) { return (__bridge_transfer id)X; }
上面的示例可以改写为:
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); CFStringRef strUUID = CFUUIDCreateString(kCFAllocatorDefault, uuid); NSString *str = CFBridgingRelease(strUUID); //无需释放 strUUID //CFRelease(strUUID); CFRelease(uuid);
__bridge
__bridge 不改变对象所有权, 需要我们自己来管理内存, 它也是我们经常使用的方法, 从某种程度上来说, 它是上面两个方法的简化版本.
__bridge 可以将 OC 对象 与 C 指针相互转换, 示例:
//CFString -> OC 对象 CFStringRef cfString = CFStringCreateWithCString(kCFAllocatorDefault, "very", kCFStringEncodingUTF8); NSString *nsString = (__bridge NSString *)cfString; NSLog(@"CFString -> NSString: %@", nsString); CFRelease(cfString);
如果将 CFRelease(cfString)
注释掉, Xcode 的静态检测器会告诉你有内存泄露的情况, 如图:
再来另外一个例子, 如下:
//OC 对象 -> CFString NSString *nstr = @"itman"; CFStringRef cfStringRef = (__bridge CFStringRef)nstr; NSLog(@"NSString -> CFString: %@", cfStringRef); CFRelease(cfStringRef);
无论是使用 CFRelease(cfStringRef)
, 还是注释掉, 静态检测器都不会报错. 说明这种情况下, 当前的内存管理已经被 OC 对象管理.
野指针
运行下面的示例:
void *p; { NSObject *objc = [[NSObject alloc] init]; p = (__bridge void*)objc; } NSLog(@"mark: %@", (__bridge NSObject*)p);
会直接 crash, 如图:
当 objc 这个对象超出作用域范围,其内存就会被回收,接着在作用域范围外用 void *p 去访问 objc 的内存,就造成了野指针.
结合上面所说的, 我们可以让指针 p 对 objc 进行引用即 retain 操作, 修改如下:
void *p; { NSObject *objc = [[NSObject alloc] init]; //p = (__bridge void*)objc; p = (__bridge_retained void*)objc; } NSLog(@"mark: %@", (__bridge NSObject*)p); // 一定要释放 CFRelease(p);
可以正常的运行. 还可以修改为另一种方式:
void *p; { NSObject *objc = [[NSObject alloc] init]; //p = (__bridge void*)objc; //p = (__bridge_retained void*)objc; p = (void *)CFBridgingRetain(objc); } NSLog(@"mark: %@", (__bridge NSObject*)p); // 一定要释放 CFRelease(p);
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对猪先飞的支持。
相关文章
- 这篇文章主要介绍了java8如何用Stream查List对象某属性是否有重复的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-11
- 这篇文章主要介绍了R语言删除指定变量或对象的操作方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-05-06
- 这篇文章主要介绍了JavaScript预解析,对象的的相关资料,小编觉得这篇文章写的还不错,需要的朋友可以参考下,希望能够给你带来帮助...2021-11-10
如何在Spring WebFlux的任何地方获取Request对象
这篇文章主要介绍了如何在Spring WebFlux的任何地方获取Request对象,帮助大家更好的理解和使用springboot框架,感兴趣的朋友可以了解下...2021-01-26牛叉的Jquery――Jquery与DOM对象的互相转换及DOM的三种操作
只有jQuery对象才能调用jQuery类库的各种函数,同样有些dom对象的属性和方法在jQuery上也是无法调用的,不过基本上jQuery类库提供的函数包含了所有的dom操作。这就需要我们知道如何将jQuery对象和DOM的相互转换的方法。1...2015-10-30- js调试中经常会碰到输出的内容是对象而无法打印的时候,光靠alert只能打印出object标示,却不能打印出来里面的内容,甚是不方便,于是各方面整理总结了如下一个函数,能够将数组或者对象这类的结果一一打印出来,具体代码如下: fu...2015-10-21
- 这篇文章主要介绍了javascript self对象使用详解的相关资料,需要的朋友可以参考下...2016-10-20
- 本文给大家介绍如何替换json对象中的key,通过实例代码给大家介绍key的替换方法,代码也很简单,需要的朋友参考下吧...2021-06-02
- 这篇文章主要介绍了详解Javascript中的Object对象,需要的朋友可以参考下...2016-03-01
- 在web页面中,图片居左,内容居右排列,是非常常见的效果,它也就是媒体对象,它是一种抽象的样式,可以用来构建不同类型的组件。本文给大家介绍Bootstrap媒体对象的实现,感兴趣的朋友一起学习吧...2016-05-04
ActiveX部件不能创建对象:dm.dmsoft代码:800A01AD
vbs调用插件报:ActiveX部件不能创建对象,代码:800A01AD,一般是因为病毒导致dll文件丢失或者64系统问题导致,需要的朋友可以参考下...2020-06-30- 这篇文章主要为大家介绍了JavaScript事件对象,了解JavaScript事件...2016-01-26
- 这篇文章主要介绍了解决阿里代码规范检测中方法缺少javadoc注释的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-08-10
- 这篇文章主要介绍了java中的空指针异常情况以及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-02
- 高阶组件就是接受一个组件作为参数并返回一个新组件(功能增强的组件)的函数。这里需要注意高阶组件是一个函数,并不是组件,这一点一定要注意,本文给大家分享React 高阶组件HOC使用小结,一起看看吧...2021-06-13
- 下面小编就为大家分享一篇基于C#调用c++Dll结构体数组指针的问题详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-06-25
- 这篇文章主要介绍了Javascript对象,特别为大家分享了对象题目的一个坑,提供了解题思路,感兴趣的小伙伴们可以参考一下...2015-12-24
- 这篇文章主要介绍了Xml中使用foreach遍历对象实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-12-04
- 下面小编就为大家带来一篇javascript创建对象的几种模式介绍。小编觉得挺不错的,现在分享给大家,也给大家做个参考...2016-05-09
解决SpringCloud Feign传对象参数调用失败的问题
这篇文章主要介绍了解决SpringCloud Feign传对象参数调用失败的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-24