这是我参与更文挑战的第14天,活动详情查看: 更文挑战
本文为在阅读 Kingfisher 源码时的收货。
其他系列文章
题外话
前几天给 Kingfisher 提了一个 pull request,居然被喵神点名了,感觉可以开心一整年了。
优秀的开源框架,经过很多人的反复验证、查看,不断地趋于完善。作为一个新手,如果想要做出突出的贡献是很难的了。但是,我们可以从小处着手,逐渐的参与到开源框架的维护当中。比如,我们可以先从代码注释入手,看到错误的、缺少的注释,那就提交一个 pull request 吧。不要认为这些事情简单、不值得一提。我们完全可以从这些小事上逐渐提高自己的影响力,深入到开源框架的维护当中。
回调队列
在制作工具类、framework 时,有时候需要让使用者去设置在哪个队列(线程)调用完成闭包或者代理方法。之前我总是将指定的队列(线程)在各个方法中传递。
直到看到了 Kingfisher 的解决方案,让我眼前一亮。
Kingfisher 中使用 enum
来表示所有的情况,一共分成了四类:
- 在主队列中异步添加任务;
- 如果当前不是主线程,主线程,则向主队列中异步添加任务,否则直接运行;
- 不改变调用队列;
- 在指定的队列上运行回调。
/// Represents callback queue behaviors when an calling of closure be dispatched.
///
/// - asyncMain: Dispatch the calling to `DispatchQueue.main` with an `async` behavior.
/// - currentMainOrAsync: Dispatch the calling to `DispatchQueue.main` with an `async` behavior if current queue is not
/// `.main`. Otherwise, call the closure immediately in current main queue.
/// - untouch: Do not change the calling queue for closure.
/// - dispatch: Dispatches to a specified `DispatchQueue`.
public enum CallbackQueue {
/// Dispatch the calling to `DispatchQueue.main` with an `async` behavior.
case mainAsync
/// Dispatch the calling to `DispatchQueue.main` with an `async` behavior if current queue is not
/// `.main`. Otherwise, call the closure immediately in current main queue.
case mainCurrentOrAsync
/// Do not change the calling queue for closure.
case untouch
/// Dispatches to a specified `DispatchQueue`.
case dispatch(DispatchQueue)
public func execute(_ block: @escaping () -> Void) {
switch self {
case .mainAsync:
DispatchQueue.main.async { block() }
case .mainCurrentOrAsync:
DispatchQueue.main.safeAsync { block() }
case .untouch:
block()
case .dispatch(let queue):
queue.async { block() }
}
}
var queue: DispatchQueue {
switch self {
case .mainAsync: return .main
case .mainCurrentOrAsync: return .main
case .untouch: return OperationQueue.current?.underlyingQueue ?? .main
case .dispatch(let queue): return queue
}
}
}
extension DispatchQueue {
// This method will dispatch the `block` to self.
// If `self` is the main queue, and current thread is main thread, the block
// will be invoked immediately instead of being dispatched.
func safeAsync(_ block: @escaping () -> Void) {
if self === DispatchQueue.main && Thread.isMainThread {
block()
} else {
async { block() }
}
}
}
复制代码
有了上面的 CallbackQueue
,在指定工作队列和回调线程就会异常的简单:
func doSomething(callbackQueue: CallbackQueue = .untouch,
completionHandler: @escaping (Result<Void, Error>) -> Void) {
// 工作线程
let loadingQueue: CallbackQueue = .mainAsync
loadingQueue.execute {
do {
// 可能会抛出异常的操作
// 回调线程
callbackQueue.execute { completionHandler(.success(())) }
} catch {
callbackQueue.execute { completionHandler(.failure(error)) }
}
}
}
复制代码
其中 loadingQueue
为工作线程,callbackQueue
为回调线程。
主线程和主队列
CallbackQueue.mainCurrentOrAsync
不仅判断了当前是否为主线程 Thread.isMainThread
,而且判断了是否为主队列 DispatchQueue.main
。
队列不是线程,队列时用来组织任务的,我们将任务添加到队列中,系统会根据资源决定是否创建新的线程去处理队列中的任务。
iOS 中,UI 相关的操作必须放在主线程
,而且必须是主队列
中。更多信息可以查看:
RxSwift 判断是否为“主队列”。