C 指针和OC 对象之间的转换方法

 更新时间:2018年03月08日 11:10:09   作者:veryitman  
这篇文章主要给大家介绍了关于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);

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

相关文章

  • QT编写简单登录界面的实现示例

    QT编写简单登录界面的实现示例

    登陆界面是网页中常见的界面,本文主要介绍了QT编写简单登录界面的实现示例,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • 用C++类实现单向链表的增删查和反转操作方法

    用C++类实现单向链表的增删查和反转操作方法

    下面小编就为大家带来一篇用C++类实现单向链表的增删查和反转操作方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-04-04
  • C语言的Struct Hack笔记

    C语言的Struct Hack笔记

    这篇文章主要介绍了C语言的Struct Hack例子,个人的一篇笔记,需要的朋友可以参考下吧
    2014-04-04
  • Qt编写地图实现省市区域图的示例代码

    Qt编写地图实现省市区域图的示例代码

    本文主要介绍了Qt编写地图实现省市区域图的示例代码,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • C++关于引用作为函数的用法

    C++关于引用作为函数的用法

    今天小编就为大家分享一篇关于C++关于引用作为函数的用法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • CFile与CStdioFile的文件读写使用方法详解

    CFile与CStdioFile的文件读写使用方法详解

    以下是对CFile与CStdioFile的文件读写使用方法进行了详细的分析介绍,需要的朋友可以过来参考下
    2013-09-09
  • MFC LoadImage用法案例详解

    MFC LoadImage用法案例详解

    这篇文章主要介绍了MFC LoadImage用法案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • C++中string的模拟实现

    C++中string的模拟实现

    这篇文章主要为大家详细介绍了C++中string的模拟实现,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • Qt数据库应用之实现通用数据库分页

    Qt数据库应用之实现通用数据库分页

    数据库分页展示,在所有的涉及到数据库记录的项目中都是需要的。本文将利用Qt实现通用数据库的分页展示,感兴趣的小伙伴可以跟随小编学习一下
    2022-02-02
  • C++ 让函数返回数组的方法

    C++ 让函数返回数组的方法

    这篇文章主要介绍了C++ 让函数返回数组的方法,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-07-07

最新评论