【问题标题】:Return to the same queue as network call was performed返回执行网络调用时的同一队列
【发布时间】:2021-02-28 05:27:25
【问题描述】:

我执行下一个代码

let task = session.uploadTask(with: request, from: requestData.body) { data, response, error in
            if let error = error {
                DispatchQueue.main.async {
                    completion(.failure(error))
                }
            }
}

一切都清楚了,在后台进行网络请求后,我将完成返回到 .main 。但是如何处理案例,如果我不想在 .main 中调用完成,而是在启动 session.uploadTask 的线程中调用完成,因为在我的应用程序中它可能不仅仅是 .main 。

【问题讨论】:

    标签: ios swift networking queue grand-central-dispatch


    【解决方案1】:

    没有一个 GCD 机制来检索当前的调度队列,以便您以后可以向它调度。 (很久以前,曾经有一种获取当前队列的方法,但在 iOS 7 中已被弃用,即便如此,它还是“仅推荐用于调试和记录目的。”)

    如果你想在特定的调度队列上调用完成处理程序,我建议为方法提供一个显式的DispatchQueue 参数。下面,我将其默认为.main,但调用者可以随意覆盖它:

    func perform(_ request: URLRequest, with data: Data, on queue: DispatchQueue = .main, completion: @escaping (Result<T, Error>) -> Void) {
        let task = session.uploadTask(with: request, from: data) { data, response, error in
            if let error = error {
                queue.async {
                    completion(.failure(error))
                }
            }
    
            ...
        }
    
        ...
    }
    

    我知道这并不是您正在寻找的,但它是一种让调用者指定将在哪个队列上调用您的闭包的简单方法。


    如果您使用的是操作队列,您可以获取current 来确定当前的操作队列。如果有人这样做,就会在该操作队列上使用addOperation 以调用完成处理程序。

    func perform(_ request: URLRequest, with data: Data, completion: @escaping (Result<T, Error>) -> Void) {
        guard let queue = OperationQueue.current else {
            fatalError("Must be called from operation queue")
        }
    
        let task = session.uploadTask(with: request, from: data) { data, response, error in
            if let error = error {
                queue.addOperation {
                    completion(.failure(error))
                }
            }
    
            ...
        }
    
        ...
    }
    

    但是这种模式只有在调用者使用操作队列时才有效,而不是仅在使用调度队列时有效。出于这个原因,我仍然倾向于采用提供目标操作队列作为参数的模式:

    func perform(_ request: URLRequest, with data: Data, on queue: OperationQueue = .main, completion: @escaping (Result<T, Error>) -> Void) {
        let task = session.uploadTask(with: request, from: data) { data, response, error in
            if let error = error {
                queue.addOperation {
                    completion(.failure(error))
                }
            }
    
            ...
        }
    
        ...
    }
    

    【讨论】:

      【解决方案2】:

      如果您想在当前线程上调用完成,只需调用completion()

      当前线程

      completion(.failure(error))

      主线程

      DispatchQueue.main.async {
                      completion(.failure(error))
                  }
      

      后台线程

      Dispatch.global(qos: .background) {
                      completion(.failure(error))
                  }
      

      【讨论】:

        【解决方案3】:

        我使用了这个解决方案:

        guard let currentDispatch = OperationQueue.current?.underlyingQueue else {
                return
            }
        
        let task = session.uploadTask(with: request, from: requestData.body) { data, response, error in
                if let error = error {
                    currentDispatch.async {
                        completion(.failure(error))
                    }
                }
        }
        

        没有测试太多,但快速调试显示了我想要实现的目标,

        【讨论】:

        • 这显然仅在您碰巧将操作队列用于后台任务时才有效。此外,它仅在该操作队列设置了underlyingQueue 时才有效。您可以通过直接使用OperationQueue.current 来删除第二个依赖项,例如guard let queue = OperationQueue.current else { ... }queue.addOperation { ... },而不是将async 分派到其底层队列。这不仅消除了对 underlyingQueue 的依赖,而且将遵守操作队列的约束,而直接调度到底层调度队列则不会。
        猜你喜欢
        • 1970-01-01
        • 2021-11-29
        • 1970-01-01
        • 2011-04-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-23
        • 2018-04-22
        相关资源
        最近更新 更多