GCD 小笔记

下面是学习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/

猜你喜欢

转载自blog.csdn.net/weixin_33676492/article/details/90889722
gcd