synchronization - 如何同步 URLSession 任务的串行队列?

标签 synchronization grand-central-dispatch nsurlsessiondatatask urlsession

使用 XCode-8.2.1、Swift-3.0.2 和 iOS-10.2.1,

我正在尝试调用两个不同的 URLSession.shared.dataTasks(第一个是简单的 URL 请求,第二个是 POST 请求)。

由于我的第一个 dataTask 提供了第二个 dataTask 的 httpBody 中需要的结果,因此两个 URLSession.shared.dataTask 应该一个接一个地连续运行! (并且准备代码应连续运行)。

到目前为止,我尝试使用两个连续的 serialQueue.sync{}排队。但我不得不意识到代码并没有按照我想要的顺序执行。

日志中的打印语句结果如下:

Hmmmmmm 2
Hmmmmmm 1
Hmmmmmm 3

(根据需要而不是 1、2、3)!

你怎么能得到订单1、2、3??

(即如何确保第二个 dataTask 的 httpBody 可以填充来自第一个 dataTask 的结果?)

这是我的代码:(不可执行,因为 URL 已被删除 - 但你明白了)!
import UIKit

class ViewController: UIViewController {

    let serialQueue = DispatchQueue(label: "myResourceQueue")
    var stationID: Int = 0

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.


        self.serialQueue.sync {
            let myResourceURL = URL(string: "myQueryString1")
            let task = URLSession.shared.dataTask(with: myResourceURL!) { (data, response, error) in
                if (error != nil) {
                    // print(error.debugDescription)
                } else {
                    if let myData = data {
                        do {
                            let myJson = try JSONSerialization.jsonObject(with: myData, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
                            // print(myJson)
                            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                            print("Hmmmmmm 1")
                            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                        } catch {
                            // error
                        }
                    }
                }
            }
            task.resume()
        }
        self.serialQueue.sync {
            var request = URLRequest(url: URL(string: "myQueryString2")!)
            request.httpMethod = "POST"
            request.addValue("API_id", forHTTPHeaderField: "Authorization")
            request.addValue("application/xml", forHTTPHeaderField: "Content-Type")
            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            print("Hmmmmmm 2")
            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            let postString: String = "My_XML_POST_Body"
            request.httpBody = postString.data(using: .utf8)
            let task = URLSession.shared.dataTask(with: request) { data, response, error in
                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                print("Hmmmmmm 3")
                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            }
            task.resume()
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

任何帮助表示赞赏!

最佳答案

我终于找到了解决方案:

灵感来自 this answer ,我介绍了一个URLSessionDataDelegate ,以及它的委托(delegate)回调方法(即 didReceive response:didReceive data:didCompleteWithError error:

重要提示:您需要使用委托(delegate)设置您的 URLSession,以使引入的 URLSessionDelegate 的回调方法起作用:使用 URLSession(configuration: ....) 为此,如下所示:

let URLSessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: URLSessionConfig, delegate: self, delegateQueue: OperationQueue.main)

之后,您就可以开始了,即日志现在符合预期:
Hmmmmmm 1
Hmmmmmm 2
Hmmmmmm 3

这是最终代码(同样不可执行,因为 URL 已被删除 - 但你明白了)!
import UIKit

class ViewController: UIViewController, URLSessionDataDelegate {

    var stationID: Int = 0
    let URLSessionConfig = URLSessionConfiguration.default
    var session: URLSession?
    var task1: URLSessionTask?
    var task2: URLSessionTask?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.session = URLSession(configuration: URLSessionConfig, delegate: self, delegateQueue: OperationQueue.main)

        // prepare dataTask Nr 1
        let myResourceURL = URL(string: "myQueryString1")
        self.task1 = session?.dataTask(with: myResourceURL!)

        // start dataTask Nr 1 (URL-request)
        self.task1?.resume()
    }

    // Optional: Use this method if you want to get a response-size information 
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {

        // print(Int(response.expectedContentLength))
        completionHandler(URLSession.ResponseDisposition.allow)
    }

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {

        if dataTask == self.task1 {
           do {
              let myJson = try JSONSerialization.jsonObject(with: myData, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
              // print(myJson)
              // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
              print("Hmmmmmm 1")
              // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

              // prepare dataTask Nr 2
              self.task2 = self.session?.dataTask(with: self.prepareMyURLRequest())
           } catch {
              // error
           }
        } else if dataTask == self.task2 {

            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            print("Hmmmmmm 3")
            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

        } else {
            print("unknown dataTask callback")
        }
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
       if (error != nil) {
          // print(error.debugDescription)
       } else if task == self.task1 {

          // start dataTask Nr 2 (POST URL-request)
          self.task2?.resume()
       }
    }

    func prepareMyURLRequest() -> URLRequest {
        var request = URLRequest(url: URL(string: "myQueryString2")!)
        request.httpMethod = "POST"
        request.addValue("API_id", forHTTPHeaderField: "Authorization")
        request.addValue("application/xml", forHTTPHeaderField: "Content-Type")
        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        print("Hmmmmmm 2")
        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        let postString: String = "My_XML_POST_Body"
        request.httpBody = postString.data(using: .utf8)
        return request
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

关于synchronization - 如何同步 URLSession 任务的串行队列?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42056365/

相关文章:

ios - 带有 dataTaskWithRequest 的 NSURLSession 仅在应用程序启动时触发,而不是在应用程序未运行时触发

ios - 是否需要保留对 URLSessionTask 的引用?

sql-server - GUID 作为主键 - 离线 OLTP

Java - 非最终领域的同步

ios - 简单的 GCD 串行队列示例,例如使用 block 的 FIFO

objective-c - 为什么我应该为高级应用程序选择 GCD 而不是 NSOperation 和 block ?

java - 在同步方法中使用wait()和notify()

mysql - 在 XAMPP 上同步/复制 MySQL 数据库,大部分时间离线笔记本电脑和在线台式机

ios - 如何使用 GCD 确保在 swift3 应用程序中连续调用

ios - NSURLSession crash with JSON data parameter is nil while error is managed