使用GCD的时候经常要判断当前代码是在哪个队列上执行的,会发现有下面这个函数:
dispatch_queue_t dispatch_get_current_queue();
可以检测当前队列是否为同步操作所针对的队列,如果是就不派发了,直接执行块。
这个问题最好的解决办法是通过GCD所提供的功能来设定“队列特有数据”,可以把任意数据以键值对形式关联到队列中。如果根据指定的键获取不到关联数据,系统会沿着层级体系向上查找,直到找到数据或达到跟队列为止。
第一个参数是要设置数据的队列,第二个第三个参数是键和值,函数是按指针来比较键的,并不是按内容,这点和字典不同。值也是不透明的void指针,可以存放任意数据,但是必须管理该对象的内存。ARC环境下很难使用OC对象作为值。代码中使用CoreFoundation字符串作为值。
函数的最后一个参数是析构函数,当队列所占内存被系统回收,或者有新的值与键相关联时,原有的值会被移除,析构函数在此时运行。
typedef void (*dispatch_function_t)(void*)
总结:
1.dispatch_get_current_queue函数的行为常常与开发者所预期的不同。已废弃。
2.由于派发队列是按层级组织的,无法单用某个队列对象来描述“当前队列”。
3.dispatch_get_current_queue函数用于解决由不可重入的代码做引发的死锁,但是可以改用“队列特定数据”来解决。
dispatch_queue_t dispatch_get_current_queue();
iOS 6.0开始已经弃用这个函数了,它检测当前队列是不是某个特定的队列。
-(NSString *)someString {
__block NSString *localSomeString;
dispatch_sync(_syncQueue, ^{
localSomeString = _someString;
});
return localSomeString;
}
-(void)setSomeString:(NSString *)someString{
dispatch_async(_syncQueue, ^{
_someString = someString;
});
}
获取方法可能会死锁。调用获取方法的队列恰好是同步操作所针对的队列(本例中是_syncQueue),dispatch_sync就一直不会返回,直到块执行完成。但是,执行块的目标队列是当前队列,当前队列的dispatch_sync之一阻塞,它在等目标队列把这个块执行完,块就永远没有机会执行了。
可以检测当前队列是否为同步操作所针对的队列,如果是就不派发了,直接执行块。
-(NSString *)someString{
__block NSString *localSomeString;
dispatch_block_t accessorBlock = ^{
localSomeString = _someString;
};
if (dispatch_get_current_queue() == _syncQueue) {
accessorBlock();
} else {
dispatch_sync(_syncQueue, accessorBlock);
}
return localSomeString;
}
dispatch_get_current_queue方法可以处理一些简单的情况,下面这段代码有两个串行派发队列。
-(void)demo1{
dispatch_queue_t queueA = dispatch_queue_create("com.lyk.queueA", NULL);
dispatch_queue_t queueB = dispatch_queue_create("com.lyk.queueB", NULL);
dispatch_sync(queueA, ^{
dispatch_sync(queueB, ^{
dispatch_sync(queueA, ^{
//......
});
});
});
}
到最内层的派发队列时,会死锁。A在等B,B在等A,A阻塞。我们用dispatch_get_current_queue来检测。
-(void)demo2{
dispatch_queue_t queueA = dispatch_queue_create("com.lyk.queueA", NULL);
dispatch_queue_t queueB = dispatch_queue_create("com.lyk.queueB", NULL);
dispatch_sync(queueA, ^{
dispatch_sync(queueB, ^{
dispatch_block_t block = ^{};
if (dispatch_get_current_queue() == queueA) {
block();
} else {
dispatch_sync(queueA, block);
}
});
});
}
这样做依然死锁,dispatch_get_current_queue获取到的当前队列是queueB,所以结果依然执行针对queueA的同步派发操作。
这个问题最好的解决办法是通过GCD所提供的功能来设定“队列特有数据”,可以把任意数据以键值对形式关联到队列中。如果根据指定的键获取不到关联数据,系统会沿着层级体系向上查找,直到找到数据或达到跟队列为止。
-(void)demo3{
dispatch_queue_t queueA = dispatch_queue_create("com.lyk.queueA", NULL);
dispatch_queue_t queueB = dispatch_queue_create("com.lyk.queueB", NULL);
dispatch_set_target_queue(queueB, queueA);
static int kQueueSpecific;
CFStringRef queueSpecificValue = CFSTR("queueA");
dispatch_queue_set_specific(queueA, &kQueueSpecific, (void *)queueSpecificValue, (dispatch_function_t)CFRelease);
dispatch_sync(queueB, ^{
dispatch_block_t block = ^{
NSLog(@"NO deadlock!");
};
CFStringRef retrievedValue = dispatch_get_specific(&kQueueSpecific);
if (retrievedValue) {
block();
} else {
dispatch_sync(queueA, block);
}
});
}
创建了两个队列,队列B的目标队列是队列A,队列A的目标队列依然是全局并发队列。然后用dispatch_queue_set_specific,在队列A上设置键值。
第一个参数是要设置数据的队列,第二个第三个参数是键和值,函数是按指针来比较键的,并不是按内容,这点和字典不同。值也是不透明的void指针,可以存放任意数据,但是必须管理该对象的内存。ARC环境下很难使用OC对象作为值。代码中使用CoreFoundation字符串作为值。
函数的最后一个参数是析构函数,当队列所占内存被系统回收,或者有新的值与键相关联时,原有的值会被移除,析构函数在此时运行。
typedef void (*dispatch_function_t)(void*)
总结:
1.dispatch_get_current_queue函数的行为常常与开发者所预期的不同。已废弃。
2.由于派发队列是按层级组织的,无法单用某个队列对象来描述“当前队列”。
3.dispatch_get_current_queue函数用于解决由不可重入的代码做引发的死锁,但是可以改用“队列特定数据”来解决。