判斷當(dāng)前隊(duì)列的方式
- 使用
dispatch_get_current_queue
獲取當(dāng)前執(zhí)行的隊(duì)列 - 使用
dispatch_queue_set_specific & dispatch_get_specific
標(biāo)標(biāo)識(shí)獲取指定隊(duì)列 - 使用dispatch_queue_get_label獲取隊(duì)列標(biāo)簽捶闸,比較字符串判斷形纺。
注意: dispatch_get_current_queue
在iOS6
之后是棄用的柑潦,蘋果只推薦在打印中使用瘟仿,原因是容易導(dǎo)致死鎖感耙。會(huì)在下面單獨(dú)展示死鎖分析企垦。
使用dispatch_queue_set_specific
來(lái)判斷當(dāng)前隊(duì)列
實(shí)現(xiàn)方式
// 1. 創(chuàng)建一個(gè)標(biāo)識(shí)
static void * mainQueueKey = &mainQueueKey;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 2. 將標(biāo)識(shí)關(guān)聯(lián)到指定隊(duì)列上
dispatch_queue_set_specific(dispatch_get_global_queue(0, 0), mainQueueKey, mainQueueKey, NULL);
});
// 3. 在需要的時(shí)機(jī)通過(guò)以下方法判斷,返回context Data
dispatch_get_specific(mainQueueKey)
定義一個(gè)方法判斷是否為主隊(duì)列
BOOL isMainQueue() {
static void * mainQueueKey = &mainQueueKey;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dispatch_queue_set_specific(dispatch_get_main_queue(), mainQueueKey, mainQueueKey, NULL);
});
return dispatch_get_specific(mainQueueKey) == mainQueueKey;
}
API
解釋
向queue
這個(gè)隊(duì)列中設(shè)置一個(gè)key
涎永,其對(duì)應(yīng)的數(shù)據(jù)為context
斯碌。那么當(dāng)我們想判斷一個(gè)代碼塊是否被這個(gè)queue
隊(duì)列執(zhí)行時(shí)一死,就可以通過(guò)dispatch_get_specific
傳入key
來(lái)獲取context
。
void dispatch_queue_set_specific(dispatch_queue_t queue, const void *key, void *context, dispatch_function_t destructor);
* queue:需要操作的隊(duì)列傻唾,對(duì)這個(gè)隊(duì)列添加key和context data
* key: 用于標(biāo)識(shí)關(guān)聯(lián)context data數(shù)據(jù)的key,此key只作為指針進(jìn)行比較投慈,不會(huì)被間接引用承耿。因此可以使用指向特定子系統(tǒng)的靜態(tài)變量的指針或者任意其他允許您唯一標(biāo)識(shí)該值的值。不建議指定指向字符串常量的指針伪煤〖哟空值不是鍵的有效值,使用空值設(shè)置上下文數(shù)據(jù)將會(huì)被忽略抱既。
* context:與key進(jìn)行關(guān)聯(lián)的上下文數(shù)據(jù)职烧,此參數(shù)可能為NULL
* destructor: 可以用來(lái)釋放上下文數(shù)據(jù)的析構(gòu)函數(shù)。此參數(shù)可能為空防泵。如果context為空蚀之,則忽略析構(gòu)函數(shù)
由于我們初始設(shè)置了key
, context
,如果任務(wù)塊在當(dāng)前的隊(duì)列執(zhí)行就可以通過(guò)key
獲取到context
捷泞,如果不是在當(dāng)前隊(duì)列就獲取context
為NULL
void * dispatch_get_specific(const void *key);
* key: 當(dāng)前塊正在其上執(zhí)行的調(diào)度隊(duì)列關(guān)聯(lián)的key足删。鍵只作為指針進(jìn)行比較,從不取消引用锁右。不建議直接傳遞字符串常量失受。
使用dispatch_queue_get_label
判斷當(dāng)前隊(duì)列
BOOL isMainQueue() {
static const char * MainIdentifier;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
MainIdentifier = dispatch_queue_get_label(dispatch_get_main_queue());
});
const char *identifier = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
return strcmp(identifier, MainIdentifier) == 0;
}
注意:在獲取當(dāng)前隊(duì)列的標(biāo)識(shí)時(shí),使用了DISPATCH_CURRENT_QUEUE_LABEL
咏瑟,這樣依然會(huì)導(dǎo)致dispatch_get_current_queue
可能產(chǎn)生的死循環(huán)問(wèn)題拂到。
dispatch_get_current_queue
導(dǎo)致死鎖的情況分析
以下為一個(gè)使用這種方式導(dǎo)致死鎖的場(chǎng)景:
- (void)testAction {
dispatch_queue_t queueA = dispatch_queue_create("com.lyk.queueA", NULL);
dispatch_queue_t queueB = dispatch_queue_create("com.lyk.queueB", NULL);
// 設(shè)置了遷移隊(duì)列,同時(shí)queueA,queueB均為串行隊(duì)列導(dǎo)致死鎖
dispatch_set_target_queue(queueB, queueA);
dispatch_sync(queueB, ^{
dispatch_sync(queueA, ^{
NSLog(@"測(cè)試");
});
});
}
// 延伸响蕴,當(dāng)將queueA改為并行隊(duì)列就不會(huì)出現(xiàn)死鎖了谆焊,為啥?
void dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);
// 簡(jiǎn)單理解 為將object中的任務(wù)塊 轉(zhuǎn)到queue中去執(zhí)行
GCD
隊(duì)列是按照層級(jí)結(jié)構(gòu)來(lái)組織的浦夷,無(wú)法單用某個(gè)隊(duì)列對(duì)象來(lái)描述"當(dāng)前隊(duì)列"辖试。
dispatch_get_current_queue
函數(shù)可能返回與預(yù)期不一致的結(jié)果
誤用dispatch_get_current_queue
可能導(dǎo)致死鎖
參考引申:
https://www.objc.io/issues/2-concurrency/concurrency-apis-and-pitfalls/