GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权
2017-04-18 20:55
316 查看
前言
看过GCD(Grand Central Dispatch)的Apple官方文档的朋友一定见过“dispatch_set_context”和“dispatch_get_context”这两个函数,那么这两个函数该怎么用呢?
我们都知道,GCD的接口参数都是“C语言类型“的,那么,我们如何将NSObject类型(Foundation框架)的数据,传入GCD的接口呢?(即:Core Foundation和Foundation对象的转换)
本文关键字
GCD:dispatch_set_context,dispatch_get_context
__bridge,__bridge_retained,__bridge_transfer
Core Foundation, NSObject
dispatch_set(get)_context
先看看这两个函数的原型:
这里的object一般指的就是通过dispatch_queue_create创建的队列。
所以,这两个函数分别完成了将context“绑定”到特定GCD队列和从GCD队列获取对应context的任务。
什么是context
在上述函数原型中,context是一个“void类型指针”,学过C语言的朋友应该都知道,void型指针可以指向任意类型,就是说,context在这里可以是任意类型的指针。
从这里可以得知,我们可以为队列“set”任意类型的数据,并在合适的时候取出来用。
用malloc创建context并绑定到队列上
参考Apple官方的例子,我们先用传统的malloc创建context,看看如下简短例子:
上面的代码运行后如下:
看,通过为队列设置context,我们就能为队列绑定自定义的数据,然后在合适的时候取出来用。
NSObject类型的context
在Mac、iOS的开发过程中,我们大部分用的都是Foundation框架下的类,就是如NSString、NSDictionary这些NSObject类型的类。
但是上面的dispatch_set(get)_context接受的context参数是C语言类型的,即Core Foundation类型的,我们如何转换呢?
由于ARC不能管理Core Foundation Object的生命周期,所以我们必须先转换context的“类型”,以便转换内存管理权。
NSObject类型的context
在Mac、iOS的开发过程中,我们大部分用的都是Foundation框架下的类,就是如NSString、NSDictionary这些NSObject类型的类。
但是上面的dispatch_set(get)_context接受的context参数是C语言类型的,即Core Foundation类型的,我们如何转换呢?
由于ARC不能管理Core Foundation Object的生命周期,所以我们必须先转换context的“类型”,以便转换内存管理权。
__bridge
Apple已经为我们提供了用于转换的关键字,如下:
__bridge: 只做了类型转换,不修改内存管理权;
__bridge_retained(即CFBridgingRetain)转换类型,同时将内存管理权从ARC中移除,后面需要使用CFRelease来释放对象;
__bridge_transfer(即CFBridgingRelease)将Core Foundation的对象转换为Objective-C的对象,同时将内存管理权交给ARC。
重新定义context
为了方便下面的说明,我们先定义context类。
看,我们继承了dealloc方法,这样就能知道Data类型对象什么时候被释放。
需要注意的点
__bridge的转换是没有转移内存管理权的,这点要特别注意。
如果在传context对象时,用的是__bridge转换,那么context对象的内存管理权还在ARC手里,一旦当前作用域执行完,context就会被释放,而如果队列的任务用了context对象,就会造成“EXC_BAD_ACCESS”崩溃!
正确的用法
解释
在dispatch_set_context的时候用__bridge_retained转换,将context的内存管理权从ARC移除,交给我们自己管理。
在队列任务中,用dispatch_get_context获取context的时
4000
候,用__bridge转换,维持context的内存管理权不变,防止出了作用域context被释放。
最后用CFRelease释放context内存。
运行结果
由结果可知,我们的context对象在最后显式调用CFRelease才被释放。
总结
总的来说,就是合理运用__bridge_retained(transfer)关键字转换对象的内存管理权,让我们自己控制对象的生命周期。
看过GCD(Grand Central Dispatch)的Apple官方文档的朋友一定见过“dispatch_set_context”和“dispatch_get_context”这两个函数,那么这两个函数该怎么用呢?
我们都知道,GCD的接口参数都是“C语言类型“的,那么,我们如何将NSObject类型(Foundation框架)的数据,传入GCD的接口呢?(即:Core Foundation和Foundation对象的转换)
本文关键字
GCD:dispatch_set_context,dispatch_get_context
__bridge,__bridge_retained,__bridge_transfer
Core Foundation, NSObject
dispatch_set(get)_context
先看看这两个函数的原型:
所以,这两个函数分别完成了将context“绑定”到特定GCD队列和从GCD队列获取对应context的任务。
什么是context
在上述函数原型中,context是一个“void类型指针”,学过C语言的朋友应该都知道,void型指针可以指向任意类型,就是说,context在这里可以是任意类型的指针。
从这里可以得知,我们可以为队列“set”任意类型的数据,并在合适的时候取出来用。
用malloc创建context并绑定到队列上
参考Apple官方的例子,我们先用传统的malloc创建context,看看如下简短例子:
//定义context,即一个结构体 typedef struct _Data { int number; } Data; //定义队列的finalizer函数,用于释放context内存 void cleanStaff(void *context) { NSLog(@"In clean, context number: %d", ((Data *)context)->number); //释放,如果是new出来的对象,就要用delete free(context); } - (void)testBody { //创建队列 dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL); //创建Data类型context数据并初始化 Data *myData = malloc(sizeof(Data)); myData->number = 10; //绑定context dispatch_set_context(queue, myData); //设置finalizer函数,用于在队列执行完成后释放对应context内存 dispatch_set_finalizer_f(queue, cleanStaff); dispatch_async(queue, ^{ //获取队列的context数据 Data *data = dispatch_get_context(queue); //打印 NSLog(@"1: context number: %d", data->number); //修改context保存的数据 data->number = 20; }); }
上面的代码运行后如下:
NSObject类型的context
在Mac、iOS的开发过程中,我们大部分用的都是Foundation框架下的类,就是如NSString、NSDictionary这些NSObject类型的类。
但是上面的dispatch_set(get)_context接受的context参数是C语言类型的,即Core Foundation类型的,我们如何转换呢?
由于ARC不能管理Core Foundation Object的生命周期,所以我们必须先转换context的“类型”,以便转换内存管理权。
NSObject类型的context
在Mac、iOS的开发过程中,我们大部分用的都是Foundation框架下的类,就是如NSString、NSDictionary这些NSObject类型的类。
但是上面的dispatch_set(get)_context接受的context参数是C语言类型的,即Core Foundation类型的,我们如何转换呢?
由于ARC不能管理Core Foundation Object的生命周期,所以我们必须先转换context的“类型”,以便转换内存管理权。
__bridge
Apple已经为我们提供了用于转换的关键字,如下:
__bridge: 只做了类型转换,不修改内存管理权;
__bridge_retained(即CFBridgingRetain)转换类型,同时将内存管理权从ARC中移除,后面需要使用CFRelease来释放对象;
__bridge_transfer(即CFBridgingRelease)将Core Foundation的对象转换为Objective-C的对象,同时将内存管理权交给ARC。
重新定义context
为了方便下面的说明,我们先定义context类。
@interface Data : NSObject @property(assign, nonatomic) int number; @end @implementation Data //继承dealloc方法,便于观察对象何时被释放 - (void)dealloc { NSLog(@"Data dealloc..."); } @end
看,我们继承了dealloc方法,这样就能知道Data类型对象什么时候被释放。
需要注意的点
__bridge的转换是没有转移内存管理权的,这点要特别注意。
如果在传context对象时,用的是__bridge转换,那么context对象的内存管理权还在ARC手里,一旦当前作用域执行完,context就会被释放,而如果队列的任务用了context对象,就会造成“EXC_BAD_ACCESS”崩溃!
正确的用法
//定义队列的finalizer函数,用于释放context内存 void cleanStaff(void *context) { //这里用__bridge转换,不改变内存管理权 Data *data = (__bridge Data *)(context); NSLog(@"In clean, context number: %d", data.number); //释放context的内存! CFRelease(context); } - (void)testBody { //创建队列 dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL); //创建Data类型context数据并初始化 Data *myData = [Data new]; myData.number = 10; //绑定context //这里用__bridge_retained转换,将context的内存管理权从ARC移除,交由我们自己手动释放! dispatch_set_context(queue, (__bridge_retained void *)(myData)); //设置finalizer函数,用于在队列执行完成后释放对应context内存 dispatch_set_finalizer_f(queue, cleanStaff); dispatch_async(queue, ^{ //获取队列的context数据 //这里用__bridge转换,不改变内存管理权 Data *data = (__bridge Data *)(dispatch_get_context(queue)); //打印 NSLog(@"1: context number: %d", data.number); //修改context保存的数据 data.number = 20; }); }
解释
在dispatch_set_context的时候用__bridge_retained转换,将context的内存管理权从ARC移除,交给我们自己管理。
在队列任务中,用dispatch_get_context获取context的时
4000
候,用__bridge转换,维持context的内存管理权不变,防止出了作用域context被释放。
最后用CFRelease释放context内存。
运行结果
总结
总的来说,就是合理运用__bridge_retained(transfer)关键字转换对象的内存管理权,让我们自己控制对象的生命周期。
相关文章推荐
- 为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权
- 为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权
- 为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权
- 为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权-备
- 利用内存chunk充当数据buffer的泛型的双向队列的简单实现
- Redis利用swap文件将数据从内存转移到磁盘
- 利用EasyTransfer转移ESP8266中的数据
- 利用siezof运算现实存储各种数据类型使用内存空间的字节数
- 关于Transfer-Encoding: chunked类型的数据的解析
- 深入理解数据类型、变量类型属性、内存四区和指针
- 基本数据类型的成员变量放在jvm得哪块内存区域里?
- 利用FFmpeg对火眼一体摄像机的回调数据进行处理:YUV转H264,H264封装flv,所有输入都是在内存中。
- 利用XML数据绑定实现页面无刷新的数据浏览
- IOS开发之__bridge,__bridge_transfer和__bridge_retained
- __bridge,__bridge_transfer和__bridge_retained的使用和区别【转载】
- java中的各种数据类型在内存中存储的方式
- 利用 JavaScript 数据绑定实现一个简单的 MVVM 库
- sizeof运算符来获取各种数据类型在内存中所占字节数--gyy整理
- 利用构建数据类型,简化代码,提高编写效率!
- Java -- 利用反射 操作任意数组,包括对象数组 和 基本数据类型的数组