下面是学习GCD的一些小笔记,先记下来,到时候忘了还可以再回顾回顾。
Dispatch Async
耗时任务在其它队列异步执行,执行成功后返回到主线程
代码例子:
// background global queue and run the work in the closure asynchronously
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
guard let self = self else {
return
}
// do some hard thing
let image = network.fetchImage()
// update ui in main thread
DispatchQueue.main.async { [weak self] in
// update main thread imageView image when image downloaded
self?.imageView.image = image
}
}
处理读写问题
下面演示了一些单例类处理图片到 添加 和 读取 功能
代码例子:
final class PhotoManager {
/*
GCD provides an elegant solution of creating a read/write lock
using dispatch barriers. Dispatch barriers are a group of functions
acting as a serial-style bottleneck when working with concurrent queues
When you submit a DispatchWorkItem to a dispatch queue you can set flags
to indicate that it should be the only item executed on the specified
queue for that particular time. This means that all items submitted to
the queue prior to the dispatch barrier must complete before the
DispatchWorkItem will execute.
When the DispatchWorkItem‘s turn arrives, the barrier executes it and
ensures that the queue does not execute any other tasks during that time.
Once finished, the queue returns to its default implementation.
*/
private init() {}
static let shared = PhotoManager()
// create a concurrent queue
private let concurrentQueue = DispatchQueue(label: "com.zhu.xxxxxxxxxxx", attributes: DispatchQueue.Attributes.concurrent)
private var unsafePhotos: [Photo] = []
// read photos
var photos: [Photo] {
var photosCopy: [Photo] = []
// need photo value, so use sync
concurrentQueue.sync {
photosCopy = self.unsafePhotos
}
// return the value
return photosCopy
}
// add photos
func addPhoto(_ photo: Photo) {
// add a barrier to write, act like serial queue now
concurrentQueue.async(flags: .barrier) { [weak self] in
guard let self = self else { return }
// add photo
self.unsafePhotos.append(photo)
// notify main thread
DispatchQueue.main.async { [weak self] in
self?.postContentAddedNotification()
}
}
}
}
Dispatch Groups
以下是一个多张图片网络下载的示例
wait() DispatchGroup manages dispatch groups. You’ll first look at its wait method. This blocks your current thread until all the group’s enqueued tasks finish
代码例子:
func downloadPhotos(withCompletion completion: BatchPhotoDownloadingCompletionClosure?) {
// async a global queue
DispatchQueue.global(qos: .userInitiated).async {
var storedError: NSError?
// a group object
let downloadGroup = DispatchGroup()
for address in [PhotoURLString.overlyAttachedGirlfriend,
PhotoURLString.successKid,
PhotoURLString.lotsOfFaces] {
// a task start
downloadGroup.enter()
let url = URL(string: address)
let photo = DownloadPhoto(url: url!) { _, error in
if error != nil {
storedError = error
}
// complete that task
downloadGroup.leave()
}
PhotoManager.shared.addPhoto(photo)
}
// will block current thread (use DispatchQueue.global(qos: .userInitiated).async not affect main thread)
downloadGroup.wait()
// mission done
DispatchQueue.main.async {
completion?(storedError)
}
}
}
但是使用 wait() 会阻塞当前的线程,所以一个更好的方式是使用 notify
代码例子:
func downloadPhotos(withCompletion completion: BatchPhotoDownloadingCompletionClosure?) {
var storedError: NSError?
// create a dispathc group
let downloadGroup = DispatchGroup()
for address in [PhotoURLString.overlyAttachedGirlfriend,
PhotoURLString.successKid,
PhotoURLString.lotsOfFaces] {
let url = URL(string: address)
// start a task
downloadGroup.enter()
let photo = DownloadPhoto(url: url!) { _, error in
if error != nil {
storedError = error
}
// a task done
downloadGroup.leave()
}
PhotoManager.shared.addPhoto(photo)
}
// use notify work like asynchronous and not block current thread
downloadGroup.notify(queue: DispatchQueue.main) {
completion?(storedError)
}
}
任务取消
使用 DispatchWorkItem 模拟任务取消,在代码示例中演示了取消部分图片下载
代码例子:
func downloadPhotos(withCompletion completion: BatchPhotoDownloadingCompletionClosure?) {
var storedError: NSError?
// create a dispathc group
let downloadGroup = DispatchGroup()
var addresses = PhotoURLString.items
// hold dispatch blocks
var blocks: [DispatchWorkItem] = []
for index in 0..<addresses.count {
// start a task
downloadGroup.enter()
// define the work in closure
let block = DispatchWorkItem(flags: .inheritQoS) {
let address = addresses[index]
let url = URL(string: address)
let photo = DownloadPhoto(url: url!) { _, error in
if error != nil {
storedError = error
}
// a task done
downloadGroup.leave()
}
PhotoManager.shared.addPhoto(photo)
}
blocks.append(block)
// perform task on main thread one by one(help demonstrate cancel task in next)
DispatchQueue.main.async(execute: block)
}
// cancel some tasks
for block in blocks[3..<blocks.count] {
let cancel = Bool.random()
if cancel {
// Cancel block... This can only cancel blocks that are still in a queue and haven't began executing.
// You can't cancel a block in the middle of execution.
block.cancel()
// remove the canceled block from the dispatch group
downloadGroup.leave()
}
}
// use notify work like asynchronous and not block current thread
downloadGroup.notify(queue: DispatchQueue.main) {
completion?(storedError)
}
}
参考:
https://www.raywenderlich.com/5370-grand-central-dispatch-tutorial-for-swift-4-part-1-2
https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html
https://developer.apple.com/videos/play/wwdc2016/720/