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