继续学习GCD

本着虚心学习的原则,在上次看完GCD后,在码代码的过程中,经常会在dispatch后自动跳出函数列中看到带有context,但是在平常的自学过程中,都没有见到过带有context的参数。那他到底指的是啥?

带着这个好奇心,我努力的爬遍了网上我能找到的相关文档。然而相关的资料很少,最后没办法只能去苹果的官方文档上找,最终大概的了解了这个context的作用。(果然,最原始的才是最有效的。)

在官方文档中有这么一个方法void dispatch_async_f( dispatch_queue_t queue, void *context, dispatch_function_t work),这个方法和我们日常搜用的dispatch_async类似,唯一的区别的就是中间增加了context这个参数。

画外音:不是dispatch_async中用的是blockdispatch_async_f中用的是work么?

答:这两个反正都是为了引用一个代码块,实际上的作用基本上都差不多的啦

而苹果对于这个context的解释是这样的:

The application-defined context parameter to pass to the function.

将程序中定义的上下文传递给参数。

那么新的问题又来了,既然他是将上下文传递给参数,我们在OC编码的过程中,主要还是基于Foundation框架,也就是所有对象继承与NSObject对象,但是他是一个OC对象,而这个context所需要的内容是一个基于C语言的指针。那么如果将OC对象转化成C呢?

画外音:我就是不用OC,我用C的malloc也是可以的啊

你出去!

正如我们所知道的,OC是C的拓展,让C拥有了面向对象的作用,从而大大提高的程序员开发的效率。同时苹果为了更方便的让开发者使用OC这门语言,他们在iOS 5以后加入到LLVM 3.0编译器的。同时使用ARC能够解决开发者心烦的手动管理内存问题,这极大的方便了程序猿们。

而C因为没有ARC机制,所以解决前面这个问题的方法就是:

将这个需要传输的对象的内存管理,从ARC手中抢过来。让我们手动来管理,这样就可以愉快的将变量以C的形式传入进去啦~~~

那么既然要解决这个问题,我们就需要寻找,如何将内存管理从ARC中“夺”过来。

在翻遍了许多网页后,这几个函数引入我眼帘:此处参考原文

  • __bridge:只做类型转换,不修改需要管理的内存使用权
  • __bridge_retained:做类型转换Core Foundation->OC对象,同时将管理权从ARC中移除,后期需要对对象进行手动释放
  • __bridge_transfer:做类型转换OC对象->Core Foundation,同时将管理权移回ARC

首先展示显示失败的代码:

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
//Data.h
@property(nonatomic,assign)int number;
//Data.m
-(void)dealloc{
NSLog(@"the Data dealloc...");
}
//ViewController - viewDidLoad
- (void)gcdTest1{
dispatch_queue_t queue = dispatch_queue_create("helloworld", DISPATCH_QUEUE_SERIAL);
Data *myDate = [[Data alloc]init];
myDate.number = 20;
dispatch_set_context(queue, (__bridge void *)myDate);
dispatch_async(queue, ^{
Data *data = (__bridge Data*)dispatch_get_context(queue);
NSLog(@"%d",data.number);
});
myDate = nil;
}

失败原因:由于我们在这里设置为了nil也就是意味着我们将他释放,但是在线程中的data还是调用者,同时因为c中对ARC不进行操作,所以,线程中的data指向的地址出现问题,所以出现EXC_BAD_ACCESS

console中的输出为:

1
2
3
2016-07-18 21:29:35.946 GCD-OC[65571:7506303] the Data dealloc...
2016-07-18 21:29:35.946 GCD-OC[65571:7506809] 20
(出现内存问题)

正确解释的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- (void)gcdTest1{
dispatch_queue_t queue = dispatch_queue_create("helloworld", DISPATCH_QUEUE_SERIAL);
Data *myDate = [[Data alloc]init];
myDate.number = 20;
dispatch_set_context(queue, (__bridge_retained void*)myDate);
dispatch_set_finalizer_f(queue, &cleanMyMemory);
dispatch_async(queue, ^{
Data *data = (__bridge Data*)dispatch_get_context(queue);
NSLog(@"%d",data.number);
});
}
void cleanMyMemory(void *context){
Data *data = (__bridge_transfer Data*)context;
data = nil;
NSLog(@"clean the data");
}

正确的输出:

1
2
3
2016-07-18 21:39:52.148 GCD-OC[65742:7528074] 20
2016-07-18 21:39:52.149 GCD-OC[65742:7528074] the Data dealloc...
2016-07-18 21:39:52.149 GCD-OC[65742:7528074] clean the data

以上就是Context的基本使用方法,看完之后各位看官们就能愉快的将OC对象作为参数传到已经准备好的dispatch对象的执行函数中了~~~~然而具体如何使用,这个根据具体开发环境决定。