在向隊列中添加任務時,可以直接在對應的函數(shù)中添加 block。但是如果想對任務進行操作,比如監(jiān)聽任務、取消任務,就需要獲取對應的 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ù)用來設置block的標記,block參數(shù)用來設置具體的任務。flags的類型為dispatch_block_flags_t的枚舉,用于設置block的標記,定義如下: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的同時可以指定了相應的優(yōu)先級。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等級表示任務需要被立即執(zhí)行,用來在響應事件之后更新 UI,來提供好的用戶體驗。這個等級最好保持小規(guī)模。QOS_CLASS_USER_INITIATED:
user initiated等級表示任務由 UI 發(fā)起異步執(zhí)行。適用場景是需要及時結(jié)果同時又可以繼續(xù)交互的時候。QOS_CLASS_DEFAULT:
default默認優(yōu)先級QOS_CLASS_UTILITY:
utility等級表示需要長時間運行的任務,伴有用戶可見進度指示器。經(jīng)常會用來做計算,I/O,網(wǎng)絡,持續(xù)的數(shù)據(jù)填充等任務。這個任務節(jié)能。QOS_CLASS_BACKGROUND:
background等級表示用戶不會察覺的任務,使用它來處理預加載,或者不需要用戶交互和對時間不敏感的任務。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對任務進行了封裝??梢栽?DispatchWorkItem初始化方法中指定任務的內(nèi)容和優(yōu)先級。事例如下: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)聽 block 執(zhí)行結(jié)束
有時我們需要等待特定的 block 執(zhí)行完成之后,再去執(zhí)行其他任務。有兩種方法可以獲取到指定 block 執(zhí)行結(jié)束的時機。
-
object-c
在object-c中,第一種為使用下面的函數(shù):long dispatch_block_wait(dispatch_block_t block, dispatch_time_t timeout);該函數(shù)會阻塞當前線程進行等待。傳入需要設置的
block和等待時間timeout。timeout參數(shù)表示函數(shù)在等待block執(zhí)行完畢時,應該等待多久。如果執(zhí)行block所需的時間小于timeout,則返回 0,否則返回非 0 值。此參數(shù)也可以取常量DISPATCH_TIME_FOREVER,這表示函數(shù)會一直等待block執(zhí)行完,而不會超時??梢允褂?dispatch_time函數(shù)和DISPATCH_TIME_NOW常量來方便的設置具體的超時時間。
如果block執(zhí)行完成,dispatch_block_wait就會立即返回。不能使用dispatch_block_wait來等待同一個block的多次執(zhí)行全部結(jié)束;這種情況可以考慮使用dispatch_group_wait來解決。也不能在多個線程中,同時等待同一個block的結(jié)束。同一個block只能執(zhí)行一次,被等待一次。注意:因為
dispatch_block_wait會阻塞當前線程,所以不應該放在主線程中調(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(@"開始執(zhí)行"); [NSThread sleepForTimeInterval:3]; NSLog(@"結(jié)束執(zhí)行"); }); dispatch_async(allTasksQueue, block); // 等待時長,10s 之后超時 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í)行超時"); } });輸入結(jié)果:
開始執(zhí)行
結(jié)束執(zhí)行
執(zhí)行成功 -
swift 3.0
在 swift 3.0 中,只需調(diào)用DispatchWorkItem的實例方法即可等待任務結(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("開始執(zhí)行") sleep(3) print("結(jié)束執(zhí)行") }) allTesksQueue.async(execute: workItem) // 設置超時時間,超時時間為 10s let timeout = DispatchTime.now() + 10 let result = workItem.wait(timeout: timeout) if result == .success { print("執(zhí)行成功") } else { print("執(zhí)行超時") } })
輸入結(jié)果同 object-c。
第二種監(jiān)聽 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ù)接收三個參數(shù),第一個參數(shù)是需要監(jiān)視的
block,第二個參數(shù)是監(jiān)聽的block執(zhí)行結(jié)束之后要提交執(zhí)行的隊列queue,第三個參數(shù)是待加入到隊列中的block。 和dispatch_block_wait的不同之處在于:dispatch_block_notify函數(shù)不會阻塞當前線程。
例如:NSLog(@"---- 開始設置任務 ----"); dispatch_queue_t serialQueue = dispatch_queue_create("com.fyf.serialqueue", DISPATCH_QUEUE_SERIAL); // 耗時任務 dispatch_block_t taskBlock = dispatch_block_create(0, ^{ NSLog(@"開始耗時任務"); [NSThread sleepForTimeInterval:2.f]; NSLog(@"完成耗時任務"); }); dispatch_async(serialQueue, taskBlock); // 更新 UI dispatch_block_t refreshUI = dispatch_block_create(0, ^{ NSLog(@"更新 UI"); }); // 設置監(jiān)聽 dispatch_block_notify(taskBlock, dispatch_get_main_queue(), refreshUI); NSLog(@"---- 完成設置任務 ----");輸出:
---- 開始設置任務 ----
---- 完成設置任務 ----
開始耗時任務
完成耗時任務
更新 UI -
swift 3.0
在 swift 3.0 中,可以使用DispatchWorkItem的實例方法進行設置,方法如下: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("---- 開始設置任務 ----") 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("開始耗時任務") sleep(3) print("完成耗時任務") }) tasksWorkItem.notify(queue: DispatchQueue.main, execute: refreshUIWorkItem) tasksQueue.async(execute: tasksWorkItem) print("---- 完成設置任務 ----")輸出:
---- 開始設置任務 ----
---- 完成設置任務 ----
開始耗時任務
完成耗時任務
更新 UI
任務的取消
iOS8 后 GCD 支持對 dispatch block 的取消。方法如下:
-
object-c
可以使用下面的函數(shù)取消dispatch block:void dispatch_block_cancel(dispatch_block_t block);這個函數(shù)用異步的方式取消指定的
block。取消操作使將來執(zhí)行dispatch block立即返回,但是對已經(jīng)在執(zhí)行的dispatch block沒有任何影響。當一個block被取消時,它會立即釋放捕獲的資源。如果要在一個block中對某些對象進行釋放操作,在取消這個block的時候,需要確保內(nèi)存不會泄漏。例子:
dispatch_queue_t serialQueue = dispatch_queue_create("com.fyf.serialqueue", DISPATCH_QUEUE_SERIAL); // 耗時任務 dispatch_block_t firstTaskBlock = dispatch_block_create(0, ^{ NSLog(@"開始第一個任務"); [NSThread sleepForTimeInterval:1.5f]; NSLog(@"結(jié)束第一個任務"); }); // 耗時任務 dispatch_block_t secTaskBlock = dispatch_block_create(0, ^{ NSLog(@"開始第二個任務"); [NSThread sleepForTimeInterval:2.f]; NSLog(@"結(jié)束第二個任務"); }); dispatch_async(serialQueue, firstTaskBlock); dispatch_async(serialQueue, secTaskBlock); // 等待 1s,讓第一個任務開始運行 [NSThread sleepForTimeInterval:1]; dispatch_block_cancel(firstTaskBlock); NSLog(@"嘗試過取消第一個任務"); dispatch_block_cancel(secTaskBlock); NSLog(@"嘗試過取消第二個任務");輸出:
開始第一個任務
嘗試過取消第一個任務
嘗試過取消第二個任務
結(jié)束第一個任務可見
dispatch_block_cancel對已經(jīng)在執(zhí)行的任務不起作用,只能取消尚未執(zhí)行的任務。 -
swift 3.0
在 swift 3.0 中,可以使用DispatchWorkItem的實例方法進行設置,方法如下:public func cancel()事例:
let tasksQueue = DispatchQueue.init(label: "com.fyf.tasksQueue"); let firstWorkItem = DispatchWorkItem.init(block: { print("開始第一個任務") sleep(2) print("結(jié)束第一個任務") }) let secondWorkItem = DispatchWorkItem.init(block: { print("開始第二個任務") sleep(2) print("結(jié)束第二個任務") }) tasksQueue.async(execute: firstWorkItem) tasksQueue.async(execute: secondWorkItem) // 等待 1s,讓第一個任務開始運行 sleep(1) firstWorkItem.cancel() print("嘗試過取消第一個任務") secondWorkItem.cancel() print("嘗試過取消第二個任務")輸出內(nèi)容同
object-c。