在向隊(duì)列中添加任務(wù)時(shí)着逐,可以直接在對(duì)應(yīng)的函數(shù)中添加 block
呕童。但是如果想對(duì)任務(wù)進(jìn)行操作漆际,比如監(jiān)聽(tīng)任務(wù)、取消任務(wù)夺饲,就需要獲取對(duì)應(yīng)的 block
奸汇。
創(chuàng)建block
-
object-c
創(chuàng)建block
有兩種方式,第一種方式如下:dispatch_block_t dispatch_block_create(dispatch_block_flags_t flags, dispatch_block_t block);
在該函數(shù)中往声,
flags
參數(shù)用來(lái)設(shè)置block
的標(biāo)記擂找,block
參數(shù)用來(lái)設(shè)置具體的任務(wù)。flags
的類型為dispatch_block_flags_t
的枚舉浩销,用于設(shè)置block
的標(biāo)記贯涎,定義如下:DISPATCH_ENUM(dispatch_block_flags, unsigned long, DISPATCH_BLOCK_BARRIER = 0x1, DISPATCH_BLOCK_DETACHED = 0x2, DISPATCH_BLOCK_ASSIGN_CURRENT = 0x4, DISPATCH_BLOCK_NO_QOS_CLASS = 0x8, DISPATCH_BLOCK_INHERIT_QOS_CLASS = 0x10, DISPATCH_BLOCK_ENFORCE_QOS_CLASS = 0x20, );
創(chuàng)建
block
的另一種方式如下:dispatch_block_t dispatch_block_create_with_qos_class(dispatch_block_flags_t flags, dispatch_qos_class_t qos_class, int relative_priority, dispatch_block_t block);
相比于
dispatch_block_create
函數(shù),這種方式在創(chuàng)建block
的同時(shí)可以指定了相應(yīng)的優(yōu)先級(jí)撼嗓。dispatch_qos_class_t
是qos_class_t
的別名柬采,定義如下:#if __has_include(<sys/qos.h>) typedef qos_class_t dispatch_qos_class_t; #else typedef unsigned int dispatch_qos_class_t; #endif
而
qos_class_t
是一種枚舉,有以下類型:QOS_CLASS_USER_INTERACTIVE:
user interactive
等級(jí)表示任務(wù)需要被立即執(zhí)行且警,用來(lái)在響應(yīng)事件之后更新 UI粉捻,來(lái)提供好的用戶體驗(yàn)。這個(gè)等級(jí)最好保持小規(guī)模斑芜。QOS_CLASS_USER_INITIATED:
user initiated
等級(jí)表示任務(wù)由 UI 發(fā)起異步執(zhí)行肩刃。適用場(chǎng)景是需要及時(shí)結(jié)果同時(shí)又可以繼續(xù)交互的時(shí)候。QOS_CLASS_DEFAULT:
default
默認(rèn)優(yōu)先級(jí)QOS_CLASS_UTILITY:
utility
等級(jí)表示需要長(zhǎng)時(shí)間運(yùn)行的任務(wù),伴有用戶可見(jiàn)進(jìn)度指示器盈包。經(jīng)常會(huì)用來(lái)做計(jì)算沸呐,I/O,網(wǎng)絡(luò)呢燥,持續(xù)的數(shù)據(jù)填充等任務(wù)崭添。這個(gè)任務(wù)節(jié)能。QOS_CLASS_BACKGROUND:
background
等級(jí)表示用戶不會(huì)察覺(jué)的任務(wù)叛氨,使用它來(lái)處理預(yù)加載呼渣,或者不需要用戶交互和對(duì)時(shí)間不敏感的任務(wù)。QOS_CLASS_UNSPECIFIED:
unspecified
未指明
事例:
dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT); dispatch_block_t block = dispatch_block_create(0, ^{ NSLog(@"normal do some thing..."); }); dispatch_async(concurrentQuene, block); // dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_DEFAULT, 0, ^{ NSLog(@"qos do some thing..."); }); dispatch_async(concurrentQuene, qosBlock);
-
swift 3.0
swift 3.0 中使用了DispatchWorkItem
對(duì)任務(wù)進(jìn)行了封裝寞埠∑ㄖ茫可以在DispatchWorkItem
初始化方法中指定任務(wù)的內(nèi)容和優(yōu)先級(jí)。事例如下:let concurrentQueue = DispatchQueue.init(label: "concurrentQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil); let block = DispatchWorkItem.init(block: { print("normal do some thing...") }) concurrentQueue.async(execute: block); let qosBlock = DispatchWorkItem.init(qos: .default, flags: .noQoS, block: { print("qos do some thing...") })
監(jiān)聽(tīng) block 執(zhí)行結(jié)束
有時(shí)我們需要等待特定的 block
執(zhí)行完成之后仁连,再去執(zhí)行其他任務(wù)空执。有兩種方法可以獲取到指定 block
執(zhí)行結(jié)束的時(shí)機(jī)硫豆。
-
object-c
在object-c
中俘种,第一種為使用下面的函數(shù):long dispatch_block_wait(dispatch_block_t block, dispatch_time_t timeout);
該函數(shù)會(huì)阻塞當(dāng)前線程進(jìn)行等待茎用。傳入需要設(shè)置的
block
和等待時(shí)間timeout
。timeout
參數(shù)表示函數(shù)在等待block
執(zhí)行完畢時(shí)伍伤,應(yīng)該等待多久并徘。如果執(zhí)行block
所需的時(shí)間小于timeout
遣钳,則返回 0扰魂,否則返回非 0 值。此參數(shù)也可以取常量DISPATCH_TIME_FOREVER
蕴茴,這表示函數(shù)會(huì)一直等待block
執(zhí)行完劝评,而不會(huì)超時(shí)【氲恚可以使用dispatch_time
函數(shù)和DISPATCH_TIME_NOW
常量來(lái)方便的設(shè)置具體的超時(shí)時(shí)間蒋畜。
如果block
執(zhí)行完成,dispatch_block_wait
就會(huì)立即返回撞叽。不能使用dispatch_block_wait
來(lái)等待同一個(gè)block
的多次執(zhí)行全部結(jié)束姻成;這種情況可以考慮使用dispatch_group_wait
來(lái)解決。也不能在多個(gè)線程中愿棋,同時(shí)等待同一個(gè)block
的結(jié)束科展。同一個(gè)block
只能執(zhí)行一次,被等待一次糠雨。注意:因?yàn)?
dispatch_block_wait
會(huì)阻塞當(dāng)前線程才睹,所以不應(yīng)該放在主線程中調(diào)用。事例
dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT); dispatch_async(concurrentQuene, ^{ dispatch_queue_t allTasksQueue = dispatch_queue_create("allTasksQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_block_t block = dispatch_block_create(0, ^{ NSLog(@"開(kāi)始執(zhí)行"); [NSThread sleepForTimeInterval:3]; NSLog(@"結(jié)束執(zhí)行"); }); dispatch_async(allTasksQueue, block); // 等待時(shí)長(zhǎng),10s 之后超時(shí) dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)); long resutl = dispatch_block_wait(block, timeout); if (resutl == 0) { NSLog(@"執(zhí)行成功"); } else { NSLog(@"執(zhí)行超時(shí)"); } });
輸入結(jié)果:
開(kāi)始執(zhí)行
結(jié)束執(zhí)行
執(zhí)行成功 -
swift 3.0
在 swift 3.0 中琅攘,只需調(diào)用DispatchWorkItem
的實(shí)例方法即可等待任務(wù)結(jié)束垮庐,如下:public func wait() public func wait(timeout: DispatchTime) -> DispatchTimeoutResult public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult
事例:
let concurrentQueue = DispatchQueue.init(label: "concurrentQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil); concurrentQueue.async(execute: { let allTesksQueue = DispatchQueue.init(label: "allTesksQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil); let workItem = DispatchWorkItem.init(block: { print("開(kāi)始執(zhí)行") sleep(3) print("結(jié)束執(zhí)行") }) allTesksQueue.async(execute: workItem) // 設(shè)置超時(shí)時(shí)間,超時(shí)時(shí)間為 10s let timeout = DispatchTime.now() + 10 let result = workItem.wait(timeout: timeout) if result == .success { print("執(zhí)行成功") } else { print("執(zhí)行超時(shí)") } })
輸入結(jié)果同 object-c
坞琴。
第二種監(jiān)聽(tīng) block
執(zhí)行完成的方法如下:
-
object-c
在 object-c 中可用下面的函數(shù):void dispatch_block_notify(dispatch_block_t block, dispatch_queue_t queue, dispatch_block_t notification_block);
該函數(shù)接收三個(gè)參數(shù)哨查,第一個(gè)參數(shù)是需要監(jiān)視的
block
,第二個(gè)參數(shù)是監(jiān)聽(tīng)的block
執(zhí)行結(jié)束之后要提交執(zhí)行的隊(duì)列queue
剧辐,第三個(gè)參數(shù)是待加入到隊(duì)列中的block
解恰。 和dispatch_block_wait
的不同之處在于:dispatch_block_notify
函數(shù)不會(huì)阻塞當(dāng)前線程。
例如:NSLog(@"---- 開(kāi)始設(shè)置任務(wù) ----"); dispatch_queue_t serialQueue = dispatch_queue_create("com.fyf.serialqueue", DISPATCH_QUEUE_SERIAL); // 耗時(shí)任務(wù) dispatch_block_t taskBlock = dispatch_block_create(0, ^{ NSLog(@"開(kāi)始耗時(shí)任務(wù)"); [NSThread sleepForTimeInterval:2.f]; NSLog(@"完成耗時(shí)任務(wù)"); }); dispatch_async(serialQueue, taskBlock); // 更新 UI dispatch_block_t refreshUI = dispatch_block_create(0, ^{ NSLog(@"更新 UI"); }); // 設(shè)置監(jiān)聽(tīng) dispatch_block_notify(taskBlock, dispatch_get_main_queue(), refreshUI); NSLog(@"---- 完成設(shè)置任務(wù) ----");
輸出:
---- 開(kāi)始設(shè)置任務(wù) ----
---- 完成設(shè)置任務(wù) ----
開(kāi)始耗時(shí)任務(wù)
完成耗時(shí)任務(wù)
更新 UI -
swift 3.0
在 swift 3.0 中浙于,可以使用DispatchWorkItem
的實(shí)例方法進(jìn)行設(shè)置护盈,方法如下:public func notify(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, queue: DispatchQueue, execute: @escaping @convention(block) () -> Swift.Void) public func notify(queue: DispatchQueue, execute: DispatchWorkItem)
事例:
print("---- 開(kāi)始設(shè)置任務(wù) ----") let tasksQueue = DispatchQueue.init(label: "com.fyf.tasksQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil); let refreshUIWorkItem = DispatchWorkItem.init(block: { print("更新 UI") }) let tasksWorkItem = DispatchWorkItem.init(block: { print("開(kāi)始耗時(shí)任務(wù)") sleep(3) print("完成耗時(shí)任務(wù)") }) tasksWorkItem.notify(queue: DispatchQueue.main, execute: refreshUIWorkItem) tasksQueue.async(execute: tasksWorkItem) print("---- 完成設(shè)置任務(wù) ----")
輸出:
---- 開(kāi)始設(shè)置任務(wù) ----
---- 完成設(shè)置任務(wù) ----
開(kāi)始耗時(shí)任務(wù)
完成耗時(shí)任務(wù)
更新 UI
任務(wù)的取消
iOS8 后 GCD 支持對(duì) dispatch block
的取消。方法如下:
-
object-c
可以使用下面的函數(shù)取消dispatch block
:void dispatch_block_cancel(dispatch_block_t block);
這個(gè)函數(shù)用異步的方式取消指定的
block
羞酗。取消操作使將來(lái)執(zhí)行dispatch block
立即返回腐宋,但是對(duì)已經(jīng)在執(zhí)行的dispatch block
沒(méi)有任何影響。當(dāng)一個(gè)block
被取消時(shí)檀轨,它會(huì)立即釋放捕獲的資源胸竞。如果要在一個(gè)block
中對(duì)某些對(duì)象進(jìn)行釋放操作,在取消這個(gè)block
的時(shí)候参萄,需要確保內(nèi)存不會(huì)泄漏卫枝。例子:
dispatch_queue_t serialQueue = dispatch_queue_create("com.fyf.serialqueue", DISPATCH_QUEUE_SERIAL); // 耗時(shí)任務(wù) dispatch_block_t firstTaskBlock = dispatch_block_create(0, ^{ NSLog(@"開(kāi)始第一個(gè)任務(wù)"); [NSThread sleepForTimeInterval:1.5f]; NSLog(@"結(jié)束第一個(gè)任務(wù)"); }); // 耗時(shí)任務(wù) dispatch_block_t secTaskBlock = dispatch_block_create(0, ^{ NSLog(@"開(kāi)始第二個(gè)任務(wù)"); [NSThread sleepForTimeInterval:2.f]; NSLog(@"結(jié)束第二個(gè)任務(wù)"); }); dispatch_async(serialQueue, firstTaskBlock); dispatch_async(serialQueue, secTaskBlock); // 等待 1s,讓第一個(gè)任務(wù)開(kāi)始運(yùn)行 [NSThread sleepForTimeInterval:1]; dispatch_block_cancel(firstTaskBlock); NSLog(@"嘗試過(guò)取消第一個(gè)任務(wù)"); dispatch_block_cancel(secTaskBlock); NSLog(@"嘗試過(guò)取消第二個(gè)任務(wù)");
輸出:
開(kāi)始第一個(gè)任務(wù)
嘗試過(guò)取消第一個(gè)任務(wù)
嘗試過(guò)取消第二個(gè)任務(wù)
結(jié)束第一個(gè)任務(wù)可見(jiàn)
dispatch_block_cancel
對(duì)已經(jīng)在執(zhí)行的任務(wù)不起作用讹挎,只能取消尚未執(zhí)行的任務(wù)校赤。 -
swift 3.0
在 swift 3.0 中,可以使用DispatchWorkItem
的實(shí)例方法進(jìn)行設(shè)置筒溃,方法如下:public func cancel()
事例:
let tasksQueue = DispatchQueue.init(label: "com.fyf.tasksQueue"); let firstWorkItem = DispatchWorkItem.init(block: { print("開(kāi)始第一個(gè)任務(wù)") sleep(2) print("結(jié)束第一個(gè)任務(wù)") }) let secondWorkItem = DispatchWorkItem.init(block: { print("開(kāi)始第二個(gè)任務(wù)") sleep(2) print("結(jié)束第二個(gè)任務(wù)") }) tasksQueue.async(execute: firstWorkItem) tasksQueue.async(execute: secondWorkItem) // 等待 1s马篮,讓第一個(gè)任務(wù)開(kāi)始運(yùn)行 sleep(1) firstWorkItem.cancel() print("嘗試過(guò)取消第一個(gè)任務(wù)") secondWorkItem.cancel() print("嘗試過(guò)取消第二個(gè)任務(wù)")
輸出內(nèi)容同
object-c
。