《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》(第四十六条:不要使用dispatch_get_current_queue)笔记
要点如下:
1、dispatch_get_current_queue():返回的是执行当前代码的队列
测试代码如下:
- (void)test{
dispatch_queue_t queueA = dispatch_queue_create("queueA", NULL);
dispatch_queue_t queueB = dispatch_queue_create("queueB", NULL);
TPLog(@"A======%@, B=====%@",queueA, queueB);
dispatch_sync(queueA, ^{
TPLog(@"A-------%@", dispatch_get_current_queue());
dispatch_sync(queueB, ^{
TPLog(@"B-------%@", dispatch_get_current_queue());
//do something
});
});
}
打印结果如下:
2019-02-22 11:28:31.700979+0800 TPWeiShangTool[13994:640633] A======<OS_dispatch_queue_serial: queueA>, B=====<OS_dispatch_queue_serial: queueB>
2019-02-22 11:28:31.701152+0800 TPWeiShangTool[13994:640633] A-------<OS_dispatch_queue_serial: queueA>
2019-02-22 11:28:42.965341+0800 TPWeiShangTool[13994:640633] B-------<OS_dispatch_queue_serial: queueB>
上述测试代码,如果在queueB队列中do something的位置调用dispatch_sync(queueA, ...);就会造成死锁,而使用dispatch_get_current_queue却无法通过判断当前队列来避免。故引出"队列特有数据"
2、dispatch_queue_set_specific:队列特有数据,可以把任意数据以键值对的形式关联到队列中。
void dispatch_queue_set_specific(
dispatch_queue_t queue, //待设置标记的队列
const void *key, //标记的键
void *context, //标记的值。注意,这里键和值是指针,即地址,故context中可以放任何数据,但必须手动管理context的内存
dispatch_function_t destructor //析构函数,但所在队列内存被回收,或者context值改变时,会被调用
);
通过键值对,就可以判断当前执行的任务是否包含在某个队列中,因为系统会根据给定的键,沿着队列的层级体系(即父队列)进行查找键所对应的值,如果到根队列还没找到,就说明当前任务不包含在你要判断的队列中,进而可以避免(1)中描述的死锁问题
简单理解就是:给某个队列加个标记,找到这个标记就说明包含在这个队列中
示例代码:
dispatch_queue_t queueA = dispatch_queue_create("queueA", NULL);
dispatch_queue_t queueB = dispatch_queue_create("queueB", NULL);
dispatch_set_target_queue(queueB, queueA);
static int kQueueSpecific;
CFStringRef queueSpecificValue = CGSTR("queueA");
//这里使用CoreFoundation字符串,是因为ARC不会自动管理CoreFoundation对象的内存,dispatch_queue_set_specific的第三个参数(值)需要手动管理内存
dispatch_queue_set_specific(
queueA,
&kQueueSpecific,
(void *)queueSpecificValue, //需要手动管理内存
(dispatch_function_t)CFRelease //用CFRelease清理旧值
); //给queueA队列做标记
dispatch_sync(queueB, ^{
dispatch_block_t block = ^{ NSLog("No deadlock!"); };
CFStringRef retrievedValue = dispatch_get_specific(&kQueueSpecific); //根据键获取值
if(retrievedValue){ //根据键找到了值,就说明包含在queueA目标队列中
block();
}else{ //没有包含在queueA中
dispatch_sync(queueA, block);
}
})
比如UI刷新必须要回到主线程里执行,就可以考虑使用这种方式