多線程是程序開發(fā)中非常基礎(chǔ)的一個概念,大家在開發(fā)過程中應(yīng)該或多或少用過相關(guān)的東西艇炎。同時這恰恰又是一個比較棘手的概念,一切跟多線程掛鉤的東西都會變得復(fù)雜报亩。如果使用過程中對多線程不夠熟悉,很可能會埋下一些難以預(yù)料的坑井氢。
iOS中的多線程技術(shù)主要有NSThread, GCD和NSOperation弦追。他們的封裝層次依次遞增,其中
- NSThread封裝性最差花竞,最偏向于底層劲件,主要基于thread使用
- GCD是基于C的API掸哑,直接使用比較方便,主要基于task使用
- NSOperation是基于GCD封裝的NSObject對象零远,對于復(fù)雜的多線程項目使用比較方便苗分,主要基于隊列使用
上篇文章介紹了NSThread的用法,NSThread已經(jīng)屬于古董級別的東西了牵辣,欣賞一下可以摔癣,真正使用就不要麻煩他了。GCD是多線程中的新貴纬向,比起NSThread更加強大择浊,也更容易使用。由于GCD的東西比較多逾条,我會分好幾篇文章介紹琢岩,這篇文章主要介紹GCD中的queue相關(guān)知識。
dispatch_queue_t
使用GCD之后师脂,你可以不用再浪費精力去關(guān)注線程担孔,GCD會幫你管理好一切。你只需要想清楚任務(wù)的執(zhí)行方法(同步還是異步)和隊列的運行方式(串行還是并行)即可吃警。
任務(wù)是一個比較抽象的概念糕篇,表示一段用來執(zhí)行的代碼,他對應(yīng)到代碼里就是一個block或者一個函數(shù)汤徽。
隊列分為串行隊列和并行隊列:
-
串行隊列一次只能執(zhí)行一個任務(wù)。只有一個任務(wù)執(zhí)行完成之后灸撰,下一個任務(wù)才能執(zhí)行谒府,主線程就是一個串行的隊列。
-
并行隊列可以同時執(zhí)行多個任務(wù)浮毯,系統(tǒng)會維護一個線程池來保證并行隊列的執(zhí)行完疫。線程池會根據(jù)當前任務(wù)量自行安排線程的數(shù)量,以確保任務(wù)盡快執(zhí)行债蓝。
隊列對應(yīng)到代碼里是一個dispatch_queue_t
對象:
dispatch_queue_t queue;
對象就有內(nèi)存壳鹤。跟普通OC對象類似,我們可以用dispatch_retain()
和dispatch_release()
對其進行內(nèi)存管理饰迹,當一個任務(wù)加入到一個queue
中的時候芳誓,任務(wù)會retain
這個queue
,直到任務(wù)執(zhí)行完成才會release
啊鸭。
值得高興的是锹淌,iOS6之后,dispatch對象已經(jīng)支持ARC赠制,所以在ARC工程之下赂摆,我們可以不用擔心他的內(nèi)存,想怎么玩就怎么玩。
要申明一個dispatch的屬性烟号。一般情況下我們只需要用strong
即可绊谭。
@property (nonatomic, strong) dispatch_queue_t queue;
如果你是寫一個framework,framework的使用者的SDK有可能還是古董級的iOS6之前汪拥。那么你需要根據(jù)OS_OBJECT_USE_OBJC
做一個判斷是使用strong
還是assign
达传。(一般github上的優(yōu)秀第三方庫都會這么做)
#if OS_OBJECT_USE_OBJC
@property (nonatomic, strong) dispatch_queue_t queue;
#else
@property (nonatomic, assign) dispatch_queue_t queue;
#endif
async
GCD中有2個異步的API
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work);
他們都是將一個任務(wù)提交到queue中,提交之后立即返回喷楣,不等待任務(wù)的的執(zhí)行趟大。提交之后,系統(tǒng)會對queue做retain操作铣焊,任務(wù)執(zhí)行完成之后逊朽,queue再被release曲伊。兩個函數(shù)實際的功能是一樣的,唯一的區(qū)別在于dispatch_async
接受block作為參數(shù)赚哗,dispatch_async_f
接受函數(shù)。
使用dispatch_async
的時候block會被copy,在block執(zhí)行完成之后block再release,由于是系統(tǒng)持有block期丰,所以不用擔心循環(huán)引用的問題,block里面的self不需要weak
在dispatch_async_f
中,context
會作為第一個參數(shù)傳給work
函數(shù)荣病。如果work
不需要參數(shù)颊亮,context
可以傳入NULL雹有。work
參數(shù)不能傳入NULL脯厨,否則可能發(fā)生無法預(yù)料的事兒
異步是一個比較抽象的概念稼跳,簡單的說就是將任務(wù)加入到隊列中之后,立即返回摇零,不需要等待任務(wù)的執(zhí)行噪服。語言的描述比較抽象邪铲,我們用代碼加深一下對概念的理解
NSLog(@"this is main queue, i want to throw a task to global queue");
dispatch_queue_t globalQueue = dispatch_queue_create("com.liancheng.global_queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(globalQueue, ^{
// task
});
NSLog(@"this is main queue, throw task completed");
上面這段代碼,會以這樣的方式運行,紅色表示正在執(zhí)行的模塊哎垦,灰色表示未執(zhí)行或者已經(jīng)執(zhí)行完成的模塊鸳碧。
- 先在main queue中執(zhí)行第一個nslog
- dispatch_async會將block提交到globalQueue中日裙,提交成功之后立即返回
- main queue執(zhí)行第二個nslog
- 等global queue中block前面的任務(wù)執(zhí)行完成之后,block被執(zhí)行。
sync
與異步相似亿卤,GCD中同步的API也是2個
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_sync_f(dispatch_queue_t queue, void *context, dispatch_function_t work);
2個API作用相同:將任務(wù)提交到queue中,任務(wù)加入queue之后不會立即返回,等待任務(wù)執(zhí)行完成之后再返回延蟹。同sync類似缰儿,dispatch_sync
與dispatch_sync_f
唯一的區(qū)別在于dispatch_sync
接收block作為參數(shù)犁罩,block被系統(tǒng)持有碑韵,不需要對self使用weak。dispatch_sync_f
接受函數(shù)work
作為參數(shù)豁遭,context作為傳給work
函數(shù)的第一個參數(shù)。同樣捂蕴,work參數(shù)也不能傳入NULL启绰,否則會發(fā)生無法預(yù)料的事兒
同步表示任務(wù)加入到隊列中之后不會立即返回委可,等待任務(wù)完成再返回。語言的描述比較抽象卡者,我們再次用代碼加深一下對概念的理解
NSLog(@"this is main queue, i want to throw a task to global queue");
dispatch_queue_t globalQueue = dispatch_queue_create("com.liancheng.global_queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(globalQueue, ^{
// task
});
NSLog(@"this is main queue, throw task completed");
我們來看看代碼的運行方式:
- 先在main queue中執(zhí)行第一個nslog
- dispatch_sync會將block提交到global queue中,等待block的執(zhí)行
- global queue中block前面的任務(wù)執(zhí)行完成之后恒傻,block執(zhí)行
- block執(zhí)行完成之后盈厘,dispatch_sync返回
- dispatch_sync之后的代碼執(zhí)行
由于dispatch_sync需要等待block被執(zhí)行,這就非常容易發(fā)生死鎖契吉。如果一個串行隊列诡渴,使用dispatch_sync提交block到自己隊列中租悄,就會發(fā)生死鎖
dispatch_queue_t queue = dispatch_queue_create("com.liancheng.serial_queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
// 到達串行隊列
dispatch_sync(queue, ^{ //發(fā)生死鎖
});
});
dispatch_sync
的代碼執(zhí)行如圖所示
dispatch_sync需要等待block執(zhí)行完成泣棋,同時由于隊列串行,block的執(zhí)行需要等待前面的任務(wù)把敢,也就是dispatch_sync執(zhí)行完成修赞。兩者互相等待柏副,永遠也不會執(zhí)行完成割择,死鎖就這樣發(fā)生了
從這里看發(fā)生死鎖需要2個條件:
- 代碼運行的當前隊列是串行隊列
- 使用sync將任務(wù)加入到自己隊列中
如果queue是并行隊列,或者將任務(wù)加入到其他隊列中玛歌,這是不會發(fā)生死鎖的。
獲取隊列
獲取主線程隊列
主線程是我們最常用的線程译荞,GCD提供了非常簡單的獲取主線程隊列的方法吞歼。
dispatch_queue_t dispatch_get_main_queue(void)
方法不需要傳入?yún)?shù),直接返回主線程隊列糯俗。
假設(shè)我們要在主線程更新UI:
dispatch_async(dispatch_get_main_queue(), ^{
[self updateUI];
});
執(zhí)行加入到主線程隊列的block杖玲,App會調(diào)用dispatch_main()
, NSApplicationMain()
摆马,或者在主線程使用CFRunLoop
。
獲取全局隊列
除了主線程隊列蕉毯,GCD提供了幾個全局隊列,可以直接獲取使用
dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags);
dispatch_get_global_queue
方法獲取的全局隊列都是并行隊列褐着,并且隊列不能被修改,也就是說對全局隊列調(diào)用dispatch_suspend(), dispatch_resume(), dispatch_set_context()等方法無效
-
identifier
: 用以標識隊列優(yōu)先級馅扣,推薦用qos_class
枚舉作為參數(shù),也可以使用dispatch_queue_priority_t
-
flags
: 預(yù)留字段蓄喇,傳入任何非0的值都可能導(dǎo)致返回NULL
可以看到dispatch_get_global_queue
根據(jù)identifier
參數(shù)返回相應(yīng)的全局隊列。identifier
推薦使用qos_class
枚舉
__QOS_ENUM(qos_class, unsigned int,
QOS_CLASS_USER_INTERACTIVE
__QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x21,
QOS_CLASS_USER_INITIATED
__QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x19,
QOS_CLASS_DEFAULT
__QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x15,
QOS_CLASS_UTILITY
__QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x11,
QOS_CLASS_BACKGROUND
__QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x09,
QOS_CLASS_UNSPECIFIED
__QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x00,
);
這個枚舉與NSThread
中的NSQualityOfService
類似
-
QOS_CLASS_USER_INTERACTIVE
: 最高優(yōu)先級,交互級別愉烙。使用這個優(yōu)先級會占用幾乎所有的系統(tǒng)CUP和I/O帶寬步责,僅限用于交互的UI操作伦忠,比如處理點擊事件,繪制圖像到屏幕上脓匿,動畫等 -
QOS_CLASS_USER_INITIATED
: 次高優(yōu)先級陪毡,用于執(zhí)行類似初始化等需要立即返回的事件 -
QOS_CLASS_DEFAULT
: 默認優(yōu)先級,當沒有設(shè)置優(yōu)先級的時候,線程默認優(yōu)先級丐谋。一般情況下用的都是這個優(yōu)先級 -
QOS_CLASS_UTILITY
: 普通優(yōu)先級,主要用于不需要立即返回的任務(wù) -
QOS_CLASS_BACKGROUND
: 后臺優(yōu)先級萧落,用于用戶幾乎不感知的任務(wù)陨倡。 -
QOS_CLASS_UNSPECIFIED
: 未知優(yōu)先級兴革,表示服務(wù)質(zhì)量信息缺失
identifier
除了使用qos_class
枚舉蜜唾,也可以用dispatch_queue_priority_t
作為參數(shù)袁余。
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
typedef long dispatch_queue_priority_t;
dispatch_queue_priority_t
對應(yīng)到qos_class
枚舉有:
- DISPATCH_QUEUE_PRIORITY_HIGH: QOS_CLASS_USER_INITIATED
- DISPATCH_QUEUE_PRIORITY_DEFAULT: QOS_CLASS_DEFAULT
- DISPATCH_QUEUE_PRIORITY_LOW: QOS_CLASS_UTILITY
- DISPATCH_QUEUE_PRIORITY_BACKGROUND: QOS_CLASS_BACKGROUND
很多時候我們喜歡將0或者NULL傳入作為參數(shù)
dispatch_get_global_queue(NULL, NULL)
由于NULL等于0,也就是DISPATCH_QUEUE_PRIORITY_DEFAULT
棚饵,所以返回的是默認優(yōu)先級
創(chuàng)建隊列
當無法獲取到理想的隊列時噪漾,我們可以自己創(chuàng)建隊列且蓬。
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
如果未使用ARC恶阴,dispatch_queue_create
創(chuàng)建的queue在使用結(jié)束之后需要調(diào)用dispatch_release
存淫。
-
label
: 隊列的名稱桅咆,調(diào)試的時候可以區(qū)分其他的隊列 -
attr
: 隊列的屬性荚虚,dispatch_queue_attr_t
類型。用以標識隊列串行渴析,并行咆疗,以及優(yōu)先級等信息
attr
參數(shù)有三種傳值方式:
// 串行
#define DISPATCH_QUEUE_SERIAL NULL
// 并行
#define DISPATCH_QUEUE_CONCURRENT \
DISPATCH_GLOBAL_OBJECT(dispatch_queue_attr_t, \
_dispatch_queue_attr_concurrent)
// 自定義屬性值
dispatch_queue_attr_t dispatch_queue_attr_make_with_qos_class(dispatch_queue_attr_t attr, dispatch_qos_class_t qos_class, int relative_priority);
DISPATCH_QUEUE_SERIAL
或者NULL
,表示創(chuàng)建串行隊列迅皇,優(yōu)先級為目標隊列優(yōu)先級。DISPATCH_QUEUE_CONCURRENT
表示創(chuàng)建并行隊列挺据,優(yōu)先級也為目標隊列優(yōu)先級暇检。
dispatch_queue_attr_make_with_qos_class
函數(shù)可以創(chuàng)建帶有優(yōu)先級的dispatch_queue_attr_t對象构蹬。通過這個對象可以自定義queue的優(yōu)先級。
-
attr
: 傳入DISPATCH_QUEUE_SERIAL
藻烤、NULL
或者DISPATCH_QUEUE_CONCURRENT
,表示串行或者并行 -
qos_class
: 傳入qos_class
枚舉,表示優(yōu)先級級別 -
relative_priority
: 相對于qos_class
的相對優(yōu)先級倾芝,qos_class
用于區(qū)分大的優(yōu)先級級別悬襟,relative_priority
表示大級別下的小級別。relative_priority
必須大于QOS_MIN_RELATIVE_PRIORITY
小于0割捅,否則將返回NULL。從GCD源碼中可以查到QOS_MIN_RELATIVE_PRIORITY
等于-15
使用dispatch_queue_attr_make_with_qos_class
創(chuàng)建隊列時莫瞬,需要注意,非法的參數(shù)可能導(dǎo)致dispatch_queue_attr_make_with_qos_class
返回NULL,dispatch_queue_create
傳入NULL會創(chuàng)建出串行隊列拐袜。寫代碼過程中需要確保這是否是預(yù)期的結(jié)果
設(shè)置目標隊列(2.25日更新,感謝@楊蕭玉HIT 指出問題甜攀,原文章有誤給大家致歉)
除了通過dispatch_queue_attr_make_with_qos_class
設(shè)置隊列的優(yōu)先級之外,也可以使用設(shè)置目標隊列的方法,設(shè)置隊列的優(yōu)先級彤敛。當隊列創(chuàng)建時未設(shè)置優(yōu)先級玄糟,隊列將繼承目標隊列的優(yōu)先級。(不過一般情況下還是推薦使用dispatch_queue_attr_make_with_qos_class
設(shè)置隊列的優(yōu)先級)
void dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);
調(diào)用dispatch_set_target_queue
會retain新目標隊列queue,release原有目標隊列贰军。設(shè)置目標隊列之后,block將會在目標隊列中執(zhí)行。注意:當目標隊列串行時童太,任何在目標隊列中執(zhí)行的block都會串行執(zhí)行翘贮,無論原隊列是否串行
假設(shè)有隊列A锨能、B是并行隊列,C為串行隊列倔约。A,B的目標隊列均設(shè)置為C钾军,那么A、B樱哼、C中的block在設(shè)置目標隊列之后最終都會串行執(zhí)行
例:
隊列1并行惨篱,隊列2串行
dispatch_queue_t queue1 = dispatch_queue_create("com.company.queue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("com.company.queue2", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue1, ^{ // block1
for (int i = 0; i < 5; i ++) {
NSLog(@"+++++");
}
});
dispatch_async(queue1, ^{ // block2
for (int i = 0; i < 5; i ++) {
NSLog(@"=====");
}
});
dispatch_async(queue2, ^{ // block3
for (int i = 0; i < 5; i ++) {
NSLog(@"----");
}
});
運行一下可知block1,block2,block3并行執(zhí)行
2016-02-25 15:05:20.024 TGCD[1940:99120] +++++
2016-02-25 15:05:20.024 TGCD[1940:99122] =====
2016-02-25 15:05:20.024 TGCD[1940:99121] ----
2016-02-25 15:05:20.025 TGCD[1940:99120] +++++
2016-02-25 15:05:20.025 TGCD[1940:99121] ----
2016-02-25 15:05:20.025 TGCD[1940:99122] =====
2016-02-25 15:05:20.025 TGCD[1940:99120] +++++
2016-02-25 15:05:20.025 TGCD[1940:99121] ----
2016-02-25 15:05:20.025 TGCD[1940:99122] =====
2016-02-25 15:05:20.025 TGCD[1940:99120] +++++
2016-02-25 15:05:20.025 TGCD[1940:99121] ----
2016-02-25 15:05:20.025 TGCD[1940:99122] =====
2016-02-25 15:05:20.025 TGCD[1940:99120] +++++
2016-02-25 15:05:20.025 TGCD[1940:99121] ----
2016-02-25 15:05:20.025 TGCD[1940:99122] =====
如果將隊列1的目標隊列設(shè)置為隊列2,會發(fā)生什么情況呢?
dispatch_queue_t queue1 = dispatch_queue_create("com.company.queue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("com.company.queue2", DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(queue1, queue2);
dispatch_async(queue1, ^{
for (int i = 0; i < 5; i ++) {
NSLog(@"+++++");
}
});
dispatch_async(queue1, ^{
for (int i = 0; i < 5; i ++) {
NSLog(@"=====");
}
});
dispatch_async(queue2, ^{
for (int i = 0; i < 5; i ++) {
NSLog(@"----");
}
});
block1克胳,block2捏雌,block3變?yōu)榱舜?/p>
2016-02-25 15:06:57.215 TGCD[1974:100675] +++++
2016-02-25 15:06:57.215 TGCD[1974:100675] +++++
2016-02-25 15:06:57.215 TGCD[1974:100675] +++++
2016-02-25 15:06:57.215 TGCD[1974:100675] +++++
2016-02-25 15:06:57.216 TGCD[1974:100675] +++++
2016-02-25 15:06:57.216 TGCD[1974:100675] =====
2016-02-25 15:06:57.216 TGCD[1974:100675] =====
2016-02-25 15:06:57.216 TGCD[1974:100675] =====
2016-02-25 15:06:57.216 TGCD[1974:100675] =====
2016-02-25 15:06:57.216 TGCD[1974:100675] =====
2016-02-25 15:06:57.216 TGCD[1974:100675] ----
2016-02-25 15:06:57.216 TGCD[1974:100675] ----
2016-02-25 15:06:57.216 TGCD[1974:100675] ----
2016-02-25 15:06:57.217 TGCD[1974:100675] ----
2016-02-25 15:06:57.217 TGCD[1974:100675] ----
注意不要循環(huán)設(shè)置目標隊列,如A的目標隊列為B,B的目標隊列為A宵荒。這將會導(dǎo)致無法預(yù)知的錯誤
延時
GCD中有2個延時的API
dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
void dispatch_after_f(dispatch_time_t when, dispatch_queue_t queue, void *context, dispatch_function_t work);
一定時間之后將block加入到queue中。when用于表示時間继低,如果傳入DISPATCH_TIME_NOW
會等同于dispatch_async
。另外不允許傳入DISPATCH_TIME_FOREVER
,這會永遠阻塞線程焙压。
通前面其他方法類似。dispatch_after
接收block作為參數(shù),系統(tǒng)持有block绰沥,block中self不需要weak。dispatch_after_f
接收work函數(shù)作為參數(shù),context作為work函數(shù)的第一個參數(shù)
需要注意的是這里的延時是不精確的,因為加入隊列不一定會立即執(zhí)行。延時1s可能會1.5s甚至2s之后才會執(zhí)行。
dispatch_barrier
在并行隊列中鸭限,有的時候我們需要讓某個任務(wù)單獨執(zhí)行兜喻,也就是他執(zhí)行的時候不允許其他任務(wù)執(zhí)行。這時候dispatch_barrier就派上了用場。
使用dispatch_barrier將任務(wù)加入到并行隊列之后晶姊,任務(wù)會在前面任務(wù)全部執(zhí)行完成之后執(zhí)行扒接,任務(wù)執(zhí)行過程中,其他任務(wù)無法執(zhí)行们衙,直到barrier任務(wù)執(zhí)行完成
dispatch_barrier在GCD中有4個API
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_barrier_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work);
void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_barrier_sync_f(dispatch_queue_t queue, void *context, dispatch_function_t work);
如果API在串行隊列中調(diào)用珠增,將等同于dispatch_async
砍艾、dispatch_async_f
蒂教、dispatch_sync
、dispatch_sync_f
脆荷,不會有任何影響凝垛。
dispatch_barrier
最典型的使用場景是讀寫問題,NSMutableDictionary在多個線程中如果同時寫入蜓谋,或者一個線程寫入一個線程讀取梦皮,會發(fā)生無法預(yù)料的錯誤。但是他可以在多個線程中同時讀取桃焕。如果多個線程同時使用同一個NSMutableDictionary剑肯。怎樣才能保護NSMutableDictionary不發(fā)生意外呢?
- (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey
{
dispatch_barrier_async(self.concurrentQueue, ^{
[self.mutableDictionary setObject:anObject forKey:aKey];
});
}
- (id)objectForKey:(id)aKey
{
__block id object = nil;
dispatch_sync(self.concurrentQueue, ^{
object = [self.mutableDictionary objectForKey:aKey];
});
return object;
}
當NSMutableDictionary寫入的時候观堂,我們使用dispatch_barrier_async让网,讓其單獨執(zhí)行寫入操作,不允許其他寫入操作或者讀取操作同時執(zhí)行师痕。當讀取的時候溃睹,我們只需要直接使用dispatch_sync,讓其正常讀取即可胰坟。這樣就可以保證寫入時不被打擾因篇,讀取時可以多個線程同時進行
set_specific & get_specific
有時候我們需要將某些東西關(guān)聯(lián)到隊列上,比如我們想在某個隊列上存一個東西,或者我們想?yún)^(qū)分2個隊列竞滓。GCD提供了dispatch_queue_set_specific
方法咐吼,通過key,將context關(guān)聯(lián)到queue上
void dispatch_queue_set_specific(dispatch_queue_t queue, const void *key, void *context, dispatch_function_t destructor);
-
queue
:需要關(guān)聯(lián)的queue商佑,不允許傳入NULL -
key
:唯一的關(guān)鍵字 -
context
:要關(guān)聯(lián)的內(nèi)容锯茄,可以為NULL -
destructor
:釋放context
的函數(shù),當新的context被設(shè)置時莉御,destructor
會被調(diào)用
有存就有取撇吞,將context關(guān)聯(lián)到queue上之后,可以通過dispatch_queue_get_specific
或者dispatch_get_specific
方法將值取出來礁叔。
void *dispatch_queue_get_specific(dispatch_queue_t queue, const void *key);
void *dispatch_get_specific(const void *key);
-
dispatch_queue_get_specific
: 根據(jù)queue和key取出context牍颈,queue參數(shù)不能傳入全局隊列 -
dispatch_get_specific
: 根據(jù)唯一的key取出當前queue的context。如果當前queue沒有key對應(yīng)的context琅关,則去queue的target queue取煮岁,取不著返回NULL,如果對全局隊列取涣易,也會返回NULL
iOS6之后dispatch_get_current_queue()
被廢棄(廢棄的原因這里不多解釋画机,如果想了解可以看這里),如果我們需要區(qū)分不同的queue新症,可以使用set_specific
方法步氏。根據(jù)對應(yīng)的key是否有值來區(qū)分
END
節(jié)后第一彈,queue相關(guān)的內(nèi)容就介紹到這里徒爹,GCD的東西挺多荚醒,其他東西之后如果有時間我會慢慢介紹,敬請期待
About Me
歡迎大家關(guān)注我隆嗅,共同學習iOS~