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);

总结

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

(0)

相关推荐

  • C语言的指针类型详细解析

    指针存储了内存的地址,同时指针是有类型的,如int*,float*,那么,一个自然的猜想就是指针变量应该存储这两方面的信息:地址和指针类型,比如,就像下面的结构体: 复制代码 代码如下: struct pointer{    long address;    int type;} 举个例子:打印sizeof(int*),值为4,可见4字节是存储内存地址用的,反过来就说明指针并没有存储类型信息的地方,那么指针的类型信息存放在哪儿呢?下面剖析一段简单的代码. 复制代码 代码如下: // ma.cpp

  • C语言入门之指针用法教程

    本文针对C语言初学者详细讲述了指针的用法,并配以实例进行说明.具体分析如下: 对于C语言初学者来说,需要明白指针是啥?重点就在一个"指"上.指啥?指的地址.啥地址?内存的地址. 上面说明就是指针的本质了. 这里再详细解释下.数据存起来是要存在内存里面的,就是在内存里圈出一块地,在这块地里放想放的东西.变量关心的是这块地里放的东西,并不关心它在内存的哪里圈的地:而指针则关心这块地在内存的哪个地方,并不关心这块地多大,里面存了什么东西. 指针怎么用呢?下面就是基本用法: int a, b,

  • C++中的对象指针总结

    指向对象的指针在建立对象的时候,变异系统会给每一个对象分配一定的存储空间,以存放其成员.对象空间的起始地址就是对象的指针.可以定义一个指针变量,用来存放对象的指针. 一个简单的示例1.1: 复制代码 代码如下: #include<iostream>using namespace std;class Student{ public:  int num;  int score;  Student(int ,int );//声明构造函数  void Print();//声明输出信息函数};Stude

  • C/C++指针和取地址的方法

    先看下面的程序: 复制代码 代码如下: void main() {     int a = 100;     int *ap = &a;     printf("%p\n",&a);//输出:002AF744     printf("%p\n",ap);//输出:002AF744     printf("%d\n",*ap);//输出:100     printf("%p\n",&ap);//输出:00

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

    Core Foundation 框架 Core Foundation 框架 (CoreFoundation.framework) 是一组 C 语言接口, 简称 CF. 它们为 iOS 应用程序提供基本数据管理和服务功能. 如 Core Graphics.Core Text,并且我们可能需要将 CF 对象和OC 对象进行相互转化,ARC 下,编译器不会自动管理 CF 对象的内存,我们需要手动管理. 创建一个 CF 对象使用后, 需要使用 CFRelease 将其手动释放, 换句话说, Core F

  • jQuery对象与DOM对象之间的转换方法

    什么是jQuery对象? ---就是通过jQuery包装DOM对象后产生的对象.jQuery对象是jQuery独有的,其可以使用jQuery里的方法. 比如: $("#test").html() 意思是指:获取ID为test的元素内的html代码.其中html()是jQuery里的方法 这段代码等同于用DOM实现代码: document.getElementById("id").innerHTML; 虽然jQuery对象是包装DOM对象后产生的,但是jQuery无法

  • c++中数字与字符串之间的转换方法(推荐)

    1.字符串数字之间的转换 (1)string --> char * string str("OK"); char * p = str.c_str(); (2)char * -->string char *p = "OK"; string str(p); (3)char * -->CString char *p ="OK"; CString m_Str(p); //或者 CString m_Str; m_Str.Format(&q

  • C++中的函数指针与函数对象的总结

    篇一.函数指针函数指针:是指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么这个指向这个函数的函数指针便指向这个地址. 函数指针的用途是很大的,主要有两个作用:用作调用函数和做函数的参数. 函数指针的声明方法:数据类型标志符 (指针变量名) (形参列表):一般函数的声明为: int func ( int x );而一个函数指针的声明方法为:int (*func) (int x);前面的那个(*func)中括号是必要的,这会告诉编译器我们声明的是函数指针而不是声明一个具有返回型为指针

  • Python hexstring-list-str之间的转换方法

    在Python操作数据内容时,多数情况下可能遇到下面3种类型的数据处理: hexstring 如:'1C532145697A8B6F' str 如:'\x1C\x53\x21\x45\x69\x7A\x8B\x6F' list 如:[0x1C, 0x53, 0x21, 0x45, 0x69, 0x7A, 0x8B, 0x6F] 各种第三方模块(如pyDes),或者自己写的接口中,可能存在由于类型不统一需要在这3种数据中来回切换的情况. 需要用到的核心的方法如下: list() 将对象转换为lis

  • C语言中字符串与各数值类型之间的转换方法

    C语言的算法设计中,经常会需要用到字符串,而由于c语言中字符串并不是一个默认类型,其标准库stdlib设计了很多函数方便我们处理字符串与其他数值类型之间的转换. 首先放上一段展示各函数使用的代码,大家也可以copy到自己的机器上运行观察 #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int num=183; char str[3]; //itoa函数将整型转换为字符串数值类型 it

  • Go语言string,int,int64 ,float之间类型转换方法

    (1)int转string s := strconv.Itoa(i) 等价于s := strconv.FormatInt(int64(i), 10) (2)int64转string i := int64(123) s := strconv.FormatInt(i, 10) 第二个参数为基数,可选2~36 注:对于无符号整形,可以使用FormatUint(i uint64, base int) (3)string转int i, err := strconv.Atoi(s) (4)string转in

  • java对象与json对象之间互相转换实现方法示例

    本文实例讲述了java对象与json对象之间互相转换实现方法.分享给大家供大家参考,具体如下: import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import net.sf.json.JSONArray; import net.sf.json.JSONObject; public class MainClass { public st

  • jQuery Data Linking 对象与对象之间属性的关联

    支持客户端的数据绑定 ASP.NET团队最近还向jQuery社区提交了被称为"data linking"的技术,Data Linking可以帮助你实现对象与对象之间属性的关联--当其中一方发生改变时另一方也随之改变.方便的实现页面中展现的数据与实际数据对象中的数据实时同步. data linking与data-binding的理论很相近(我们之所以使用data linking这个名称是因为jQuery中已经包含bing()方法,尽管这个方法与数据绑定没有什么关系...). 现在来看看d

  • PHP数组与对象之间使用递归实现转换的方法

    本文实例讲述了PHP数组与对象之间使用递归实现转换的方法.分享给大家供大家参考.具体实现方法如下: 这里涉及一些简单的对象与数组的相互转换的问题,采用递归写了两个方法如下: function arrayToObject($e){ if( gettype($e)!='array' ) return; foreach($e as $k=>$v){ if( gettype($v)=='array' || getType($v)=='object' ) $e[$k]=(object)arrayToObj

随机推荐