重学GCD之DispatchWorkItem
DispatchWorkItem是iOS8时候推出的一套API, 增加了一些对任务控制的API. 与Operation对象或者BlockOperation比较像, 主要可以主动cancel, 并且增加了notify功能
具体的新增功能, 直接在注释里面看吧
@available(macOS 10.10, iOS 8.0, *)
public class DispatchWorkItem {
// 初始化方法 => 最关键的是 最后的 block, 实际就是 我们要执行的 TaskItem
public init(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], block: @escaping @convention(block) () -> Void)
/// sync 同步执行
public func perform()
/// 内置信号量 - 同步等待任务执行完成
public func wait()
/// 内置信号量 - 同步等待指定时间, 等待任务完成
public func wait(timeout: DispatchTime) -> DispatchTimeoutResult
public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult
/// 内置信号量, WorkItem 执行完成或者被调度, 信号量触发通知, 可以指定 barrier
public func notify(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], queue: DispatchQueue, execute: @escaping @convention(block) () -> Void)
public func notify(queue: DispatchQueue, execute: DispatchWorkItem)
/// 可以取消, 异步将 WorkItem内部的state设置层 isCancelled. 被调度立刻返回(注意, 也会触发notify)
public func cancel()
/// 判断WorkItem是否被取消的状态
public var isCancelled: Bool { get }
}
可以看出, GCD 中的DispatchWorkItem就如同OperationQueue中的BlockOperation类似的控制粒度!!! 以后在GCD中使用可以取消 workItem !!!
let workQueue = DispatchQueue(label: "com.webank.serialQueue")
var item: DispatchWorkItem
func testDispatchItems() {
let myObj = MyObj()
item = DispatchWorkItem { [weak self] in
print("item is start...")
for _ in 0 ... 5 {
if self?.item.isCancelled ?? true {
print("item isCancelled")
break
}
print(myObj.self)
self?.heavyWork()
}
print("item is end...")
}
item.notify(queue: DispatchQueue.main, execute: {
print("item?.notify(queue: DispatchQueue.main, exe!!!!")
})
workQueue.async {
print("preview task start...")
sleep(5)
print("preview task end...")
}
workQueue.async(execute: item)
// 延迟2s取消
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
self.item.cancel()
}
}
func heavyWork() {
sleep(1)
print("heavyWork")
}
基于 DispatchWorkItem 的防抖和限流
// 数据节流器
class Throttle {
private let label: String
private let interval: Int
private let queue: DispatchQueue
private var workItem: DispatchWorkItem?
private var lock: DispatchSemaphoreWrapper
private var lastTime: Date = Date()
/// interval: 单位毫秒
init(label: String = "Throttler", interval: Int = 500) {
self.label = label
self.interval = interval
self.queue = DispatchQueue(label: label)
self.lock = DispatchSemaphoreWrapper(value: 1)
}
func call(_ callback: @escaping (() -> ())) {
self.lock.sync {
self.workItem?.cancel()
self.workItem = DispatchWorkItem { [weak self] in
self?.lastTime = Date()
callback()
}
if let workItem = self.workItem {
let new = Date()
let delay = new.timeIntervalSince1970 - lastTime.timeIntervalSince1970 > Double(self.interval)/1000 ? DispatchTimeInterval.milliseconds(0) : DispatchTimeInterval.milliseconds(self.interval)
self.queue.asyncAfter(deadline: .now() + delay, execute: workItem)
}
}
}
}
// 网络防抖
class Debouncer {
private let label: String
private let interval: Int
private let queue: DispatchQueue
private var workItem: DispatchWorkItem?
private var lock: DispatchSemaphoreWrapper
/// interval: 单位毫秒
init(label: String = "Debouncer", interval: Int = 500) {
self.label = label
self.interval = interval
self.queue = DispatchQueue(label: label)
self.lock = DispatchSemaphoreWrapper(value: 1)
}
func call(_ callback: @escaping (() -> ())) {
self.lock.sync {
self.workItem?.cancel()
self.workItem = DispatchWorkItem {
callback()
}
if let workItem = self.workItem {
self.queue.asyncAfter(deadline: .now() + DispatchTimeInterval.milliseconds(interval), execute: workItem)
}
}
}
}
struct DispatchSemaphoreWrapper {
private var lock: DispatchSemaphore
init(value: Int) {
self.lock = DispatchSemaphore(value: value)
}
func sync(excute: () -> ()) {
_ = lock.wait(timeout: DispatchTime.distantFuture)
lock.signal()
excute()
}
}
具体的 测试方法如下:
func testThrottle() {
for _ in 0..<10 {
sleep(1)
throttle.call {
let date = Date()
print("XQ:(date.timeIntervalSince1970)")
}
}
}
func testDebouncer() {
for i in 0..<100 {
let delay = i % 2 == 0 ? 1 : 3
sleep(UInt32(delay))
debouncer.call {
let date = Date()
print("XQ:(date.timeIntervalSince1970)")
}
}
}