下面评论的好友“@Jim”给了种新的思路,就是在清除context的函数里面,用“_bridge_transfer”转换context,把context的内存管理权限重新交给ARC,这样,就不用显式调用“CFRelease”了。如下:

1
2
3
4
5
6
7
void cleanStaff(void *context) {
//这里用_bridge_transfer转换,将内存管理权限交还给ARC
Data *data = (_bridge_transfer Data *)(context);
NSLog(@"In clean, context number: %d", data.number); //不用显式释放context的内存!
}

前言

看过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

先看看这两个函数的原型:

1
2
3
4
//设置context
void dispatch_set_context (dispatch_object_t object, void *context);
//获取context
void* dispatch_get_context (dispatch_object_t object);

这里的object一般指的就是通过dispatch_queue_create创建的队列。

所以,这两个函数分别完成了将context“绑定”到特定GCD队列和从GCD队列获取对应context的任务。

什么是context

在上述函数原型中,context是一个“void类型指针”,学过C语言的朋友应该都知道,void型指针可以指向任意类型,就是说,context在这里可以是任意类型的指针。

从这里可以得知,我们可以为队列“set”任意类型的数据,并在合适的时候取出来用。

用malloc创建context并绑定到队列上

参考Apple官方的例子,我们先用传统的malloc创建context,看看如下简短例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//定义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)runTest {
//创建队列
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;
});
}

上面的代码运行后如下:

1
2
2015-03-29 20:28:16.854 GCDTest[37787:1443423] 1: context number: 10
2015-03-29 20:28:16.855 GCDTest[37787:1443423] In clean, context number: 20

看,通过为队列设置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类。

1
2
3
4
5
6
7
8
9
10
11
12
@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”崩溃!

正确的用法

重写上面的例子,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//定义队列的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的时候,用__bridge转换,维持context的内存管理权不变,防止出了作用域context被释放。
  • 最后用CFRelease释放context内存。

运行结果

1
2
3
2015-03-29 21:12:41.631 GCDTest[38131:1465900] 1: context number: 10
2015-03-29 21:12:41.632 GCDTest[38131:1465900] In clean, context number: 20
2015-03-29 21:12:41.632 GCDTest[38131:1465900] Data dealloc...

由结果可知,我们的context对象在最后显式调用CFRelease才被释放。

总结

总的来说,就是合理运用__bridge_retained(transfer)关键字转换对象的内存管理权,让我们自己控制对象的生命周期。

为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权-备的更多相关文章

  1. 将如下三组不同类型的数据利用DataInputStream和DataOutputStream写入文件,然后从文件中读出

    三组数据如下: {19.99 , 9.99 , 15.99 , 3.99 , 4.99} {12 , 8 , 13 ,29 ,50} {"Java T-shirt" , " ...

  2. springMVC参数绑定JSON类型的数据

    需求就是: 现在保存一个Student,并且保存Student的friend,一个student会有多个朋友,这里要传递到后台的参数是: var friends = new Array(); var ...

  3. GCD使用经验与技巧浅谈

    前言 GCD(Grand Central Dispatch)可以说是Mac.iOS开发中的一大“利器”,本文就总结一些有关使用GCD的经验与技巧. dispatch_once_t必须是全局或stati ...

  4. 唐巧的iOS技术博客选摘

    1. 那些被遗漏的objective-c保留字:http://blog.devtang.com/blog/2013/04/29/the-missing-objc-keywords/   2. 使用cr ...

  5. Disruptor——一种可替代有界队列完成并发线程间数据交换的高性能解决方案

    本文翻译自LMAX关于Disruptor的论文,同时加上一些自己的理解和标注.Disruptor是一个高效的线程间交换数据的基础组件,它使用栅栏(barrier)+序号(Sequencing)机制协调 ...

  6. TreeView树形控件递归绑定数据库里的数据

    TreeView树形控件递归绑定数据库里的数据. 第一种:性能不好 第一步:数据库中查出来的表,字段名分别为UNAME(显示名称),DID(关联数据),UTYPE(类型) 第二步:前台代码 <% ...

  7. iOS边练边学--GCD的基本使用、GCD各种队列、GCD线程间通信、GCD常用函数、GCD迭代以及GCD队列组

    一.GCD的基本使用 <1>GCD简介 什么是GCD 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数   GCD的优势 G ...

  8. Windows Phone 8初学者开发—第14部分:在运行时绑定到真实的数据

    原文 Windows Phone 8初学者开发—第14部分:在运行时绑定到真实的数据 第14部分:在运行时绑定到真实的数据 原文地址: http://channel9.msdn.com/Series/ ...

  9. 学习笔记TF049:TensorFlow 模型存储加载、队列线程、加载数据、自定义操作

    生成检查点文件(chekpoint file),扩展名.ckpt,tf.train.Saver对象调用Saver.save()生成.包含权重和其他程序定义变量,不包含图结构.另一程序使用,需要重新创建 ...

随机推荐

  1. 模糊语意变数、规则和模糊运算--AForge.NET框架的使用(二)

    原文:模糊语意变数.规则和模糊运算--AForge.NET框架的使用(二) 语意变数(Linguistic Variable) 语意变数存储了数个语意量(标签),每个语意量包含一个识别名和模糊集合.在 ...

  2. Linux企业级项目实践之网络爬虫(26)——线程池

    一旦有一个抓取请求开始,就创建一个新的线程,由该线程执行任务,任务执行完毕之后,线程就退出.这就是"即时创建,即时销毁"的策略.尽管与创建进程相比,创建线程的时间已经大大的缩短,但 ...

  3. 黑马程序员_JavaIO流(三)

    字节流File读写操作 字符流: FileReader FileWriter BufferedReader BufferedWrtier 字节流: FileInputStream FileOutput ...

  4. Java设计模式之简单工厂、工厂方法和抽象工厂

    在前面的学习中(参见前面的博客),我们学到了很多OO原则: 封装变化 多用组合,少用继承 针对接口/超类编程,不针对实现编程 松耦合 开闭原则 让我们从一个简单的类开始,看看如何将之改造成符合OO原则 ...

  5. D - Network - poj3694(LCA求桥)

    题意:有一个网络有一些边相互连接,现在有Q次操作,求每次操作后的桥的个数 分析:开始竟然不知道还有LCA这么个东西....... *********************************** ...

  6. 设计模式&UML学习

    1. 1.1 1.2 2. 2.1 2.2 3.参考文档 [1] 陈金荣:http://blog.csdn.net/cjr15233661143/article/details/8532997 [2] ...

  7. 【机器学习算法-python实现】Adaboost的实现(1)-单层决策树(decision stump)

    (转载请注明出处:http://blog.csdn.net/buptgshengod) 1.背景      上一节学习支持向量机,感觉公式都太难理解了,弄得我有点头大.只是这一章的Adaboost线比 ...

  8. UVA 714 Copying Books 最大值最小化问题 (贪心 + 二分)

      Copying Books  Before the invention of book-printing, it was very hard to make a copy of a book. A ...

  9. 新唐Cortex-M0微控制器的省电管理

    新唐科技的NUC1xx包含NUC101.NUC100.NUC120.NUC130和NUC140,是以ARM Cortex-M0为核心的32位微控制器(MCU),经由不同时钟的设定,最高可以达到 50M ...

  10. DataGrid( 数据表格) 组件[4]

    本节课重点了解 EasyUI 中 DataGrid(数据表格)组件的使用方法,这个组件依赖于Panel(面板).Resizeable(调整大小).LinkButton(按钮).Pageination( ...