即使设置了操作的优先级和依赖关系,操作队列也不会按顺序执行

Shi*_*thi 5 queue nsoperationqueue ios swift

我正在进行三个 api 调用,并希望 API1 应首先执行,完成后应执行 API2,然后执行 API3。我为此使用了操作队列,并添加了对操作的依赖性。我也尝试设置优先级,但没有按顺序获取 api 调用。帮我看看如何正确制作。

代码是这样的:

let op1 = Operation()
op1.completionBlock = {
    self.APICall(urlString: self.url1)
}
op1.queuePriority = .veryHigh

let op2 = Operation()
op2.completionBlock = {
    self.APICall(urlString: self.url2)
}
op2.queuePriority = .high

let op3 = Operation()
op3.completionBlock = {
    self.APICall(urlString: self.url3)
}

op3.queuePriority = .normal

op2.addDependency(op1)
op3.addDependency(op2)

queue.addOperations([op1, op2, op3], waitUntilFinished: false)
Run Code Online (Sandbox Code Playgroud)

我将 API 调用方法放在 DispatchQueue.main.sync 中,如下所示:

func APICall(urlString: String) {

    let headers: HTTPHeaders = [
        "Accept": "text/html"
    ]
    print(urlString)
    DispatchQueue.main.sync {

        Alamofire.request(urlString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: headers).responseJSON {
            response in
            // self.stopActivityIndicator()
            print(response.result.value)
            switch response.result {
            case .success:
                break
            case .failure(let error):
                break
            }
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

Rob*_*Rob 6

有几个问题:

\n\n
    \n
  1. 如果您\xe2\x80\x99 尝试管理操作之间的依赖关系,则无法将操作\xe2\x80\x99s 用于completionBlock依赖关系所依赖的代码。直到操作完成后才会调用完成块(从而破坏了任何依赖项的目的)。

    \n\n

    因此以下内容将无法按预期工作:

    \n\n
    let queue = OperationQueue()\n\nlet op1 = Operation()\nop1.completionBlock = {\n    print("starting op1")\n    Thread.sleep(forTimeInterval: 1)\n    print("finishing op1")\n}\n\nlet op2 = Operation()\nop2.completionBlock = {\n    print("starting op2")\n    Thread.sleep(forTimeInterval: 1)\n    print("finishing op2")\n}\n\nop2.addDependency(op1)\n\nqueue.addOperations([op1, op2], waitUntilFinished: false)\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    但如果你像这样定义操作,它就会起作用:

    \n\n
    let op1 = BlockOperation() {\n    print("starting op1")\n    Thread.sleep(forTimeInterval: 1)\n    print("finishing op1")\n}\n\nlet op2 = BlockOperation {\n    print("starting op2")\n    Thread.sleep(forTimeInterval: 1)\n    print("finishing op2")\n}\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    (但这只是因为我重新定义了同步操作才有效。请参阅下面的第 3 点。)

    \n\n

    值得注意的是,通常您从不Operation直接使用它\xe2\x80\x99。正如文档所说

    \n\n
    \n

    表示与单个任务关联的代码和数据的抽象类。...

    \n\n

    因为该类Operation是一个抽象类,所以您不直接使用它,而是子类化或使用系统定义的子类之一(NSInvocationOperationBlockOperation)来执行实际任务。

    \n
    \n\n

    因此使用BlockOperation上面的 , 或对其进行子类化,如下面第 3 点所示。

  2. \n
  3. 如果必须严格遵守操作执行的顺序,则不应使用优先级来管理操作执行的顺序。正如queuePriority 文档所说(强调):

    \n\n
    \n

    该值用于影响操作出列和执行的顺序...

    \n\n

    您应该仅根据需要使用优先级值来对非相关操作的相对优先级进行分类。不应使用优先级值来实现不同操作对象之间的依赖关系管理。如果需要在操作之间建立依赖关系,请使用addDependency(_:)方法。

    \n
    \n\n

    因此,如果您将 100 个高优先级操作和 100 个默认优先级操作排队,那么您就不会所有高优先级操作都会在较低优先级操作开始运行之前启动。它会倾向于优先考虑它们,但并非严格如此。

  4. \n
  5. 第一点是没有意义的,因为您正在调用异步方法。所以你可以\xe2\x80\x99t使用简单的OperationBlockOperation. 如果您不想\xe2\x80\x99 在前一个网络请求完成之前启动后续网络请求,则\xe2\x80\x99 将希望将这些网络请求包装在自定义异步中Operation类中,其中包含所有特殊的 KVO:

    \n\n
    class NetworkOperation: AsynchronousOperation {\n    var request: DataRequest\n\n    static var sessionManager: SessionManager = {\n        let manager = Alamofire.SessionManager(configuration: .default)\n        manager.startRequestsImmediately = false\n        return manager\n    }()\n\n    init(urlString: String, parameters: [String: String]? = nil, completion: @escaping (Result<Any>) -> Void) {\n        let headers: HTTPHeaders = [\n            "Accept": "text/html"\n        ]\n\n        let string = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!\n        let url = URL(string: string)!\n        request = NetworkOperation.sessionManager.request(url, parameters: parameters, headers: headers)\n\n        super.init()\n\n        request.responseJSON { [weak self] response in\n            completion(response.result)\n            self?.finish()\n        }\n    }\n\n    override func main() {\n        request.resume()\n    }\n\n    override func cancel() {\n        request.cancel()\n    }\n}\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    然后你可以这样做:

    \n\n
    let queue = OperationQueue()\n\nlet op1 = NetworkOperation(urlString: ...) { result in\n    ...\n}\n\nlet op2 = NetworkOperation(urlString: ...) { result in\n    ...\n}\n\nlet op3 = NetworkOperation(urlString: ...) { result in\n    ...\n}\n\nop2.addDependency(op1)\nop3.addDependency(op2)\n\nqueue.addOperations([op1, op2, op3], waitUntilFinished: false)\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    因为 \xe2\x80\x99s 使用AsynchronousOperation子类(如下所示),因此在异步请求完成之前,操作将\xe2\x80\x99 无法完成。

    \n\n
    /// Asynchronous operation base class\n///\n/// This is abstract to class performs all of the necessary KVN of `isFinished` and\n/// `isExecuting` for a concurrent `Operation` subclass. You can subclass this and\n/// implement asynchronous operations. All you must do is:\n///\n/// - override `main()` with the tasks that initiate the asynchronous task;\n///\n/// - call `completeOperation()` function when the asynchronous task is done;\n///\n/// - optionally, periodically check `self.cancelled` status, performing any clean-up\n///   necessary and then ensuring that `finish()` is called; or\n///   override `cancel` method, calling `super.cancel()` and then cleaning-up\n///   and ensuring `finish()` is called.\n\npublic class AsynchronousOperation: Operation {\n\n    /// State for this operation.\n\n    @objc private enum OperationState: Int {\n        case ready\n        case executing\n        case finished\n    }\n\n    /// Concurrent queue for synchronizing access to `state`.\n\n    private let stateQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".rw.state", attributes: .concurrent)\n\n    /// Private backing stored property for `state`.\n\n    private var _state: OperationState = .ready\n\n    /// The state of the operation\n\n    @objc private dynamic var state: OperationState {\n        get { stateQueue.sync { _state } }\n        set { stateQueue.sync(flags: .barrier) { _state = newValue } }\n    }\n\n    // MARK: - Various `Operation` properties\n\n    open         override var isReady:        Bool { return state == .ready && super.isReady }\n    public final override var isAsynchronous: Bool { return true }\n    public final override var isExecuting:    Bool { return state == .executing }\n    public final override var isFinished:     Bool { return state == .finished }\n\n    // KVN for dependent properties\n\n    open override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {\n        if ["isReady", "isFinished", "isExecuting"].contains(key) {\n            return [#keyPath(state)]\n        }\n\n        return super.keyPathsForValuesAffectingValue(forKey: key)\n    }\n\n    // Start\n\n    public final override func start() {\n        if isCancelled {\n            state = .finished\n            return\n        }\n\n        state = .executing\n\n        main()\n    }\n\n    /// Subclasses must implement this to perform their work and they must not call `super`. The default implementation of this function throws an exception.\n\n    open override func main() {\n        fatalError("Subclasses must implement `main`.")\n    }\n\n    /// Call this function to finish an operation that is currently executing\n\n    public final func finish() {\n        if !isFinished { state = .finished }\n    }\n}\n
    Run Code Online (Sandbox Code Playgroud)
  6. \n
  7. 作为一个非常小的观察,您的代码指定了带有 JSON 参数的 GET 请求。这没有意义。GET 请求没有可以包含 JSON 的正文。GET 请求仅使用 URL 编码。除此之外,您\xe2\x80\x99没有传递任何参数。

  8. \n
\n