在操作完成之前调用 completionBlock

completionBlock be called before the operation done

我这里有一些代码:

let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
let types: [FeedTimeline] = [.special, .pr, .pickup, .lastPost]
var operations: [Operation] = []
for type in types {
            
    let operation = BlockOperation {
        print("Working")
        self.getArticles(of: type, page: 1, completion: { (articles) in
            print("Fetched")
        })
    }
    operation.completionBlock = {
        print("Done")
    }
    queue.addOperation(operation)

在我 运行 上面的代码之后,我得到以下结果:

Working

Done

Working

Working

Done

Done

Working

Done

一段时间后,我得到了 4 次“Fetched”。这是为什么?如果 API 请求 (getArticles) 完成,如何使 completionBlock 仅 运行。

我想要的是:working -> fetched -> done -> working ....等等

此行为的原因是一旦对 self.getArticles 的调用被执行,操作将继续执行(即退出),而不是在它完成之后。

解决此问题的一种方法是使用 dispatch groups。试试下面的代码:

    let queue = OperationQueue()
    let group = DispatchGroup()
    queue.maxConcurrentOperationCount = 1
    let types: [FeedTimeline] = [.special, .pr, .pickup, .lastPost]
    var operations: [Operation] = []

    for type in types {

        let operation = BlockOperation {
            group.enter()
            print("Working")
            self.getArticles(of: type, page: 1, completion: { (articles) in
                print("Fetched")
                group.leave()
            })
            group.wait(timeout: DispatchTime.distantFuture)
        }
        operation.completionBlock = {
            print("Done")
        }
        queue.addOperation(operation)

在这种情况下,对 group.wait() 的调用将阻塞,直到对 group.enter() 的每个调用都与对 group.leave() 的调用相匹配,这意味着 BlockOperation 将等到对 getArticles 的调用完成。

注意需要保证getArticles一直执行回调,如果不执行(比如网络超时),操作块将永远挂起。

您需要使用 Operation 的子 class 并覆盖 isExecutingisFinished 来告诉系统您的操作何时开始和何时结束。像这样:

class AsyncOperation: Operation {
    override var isAsynchronous: Bool {
        return true
    }

    private let _queue = DispatchQueue(label: "asyncOperationQueue", attributes: .concurrent)
    private var _isExecuting: Bool = false

    override var isExecuting: Bool {
        set {
            willChangeValue(forKey: "isExecuting")
            _queue.async(flags: .barrier) {
                self._isExecuting = newValue
            }
            didChangeValue(forKey: "isExecuting")
        }

        get {
            return _isExecuting
        }
    }

    var _isFinished: Bool = false

    override var isFinished: Bool {
        set {
            willChangeValue(forKey: "isFinished")
            _queue.async(flags: .barrier) {
                self._isFinished = newValue
            }
            didChangeValue(forKey: "isFinished")
        }

        get {
            return _isFinished
        }
    }
}

然后从这个新的 class 子class 你的操作。我想如果你想用操作和操作队列来阅读 this 会有所帮助。