Swift 2 如何在继续代码之前等待 NSURLSessionDataTask 完成

Swift 2 How do I wait for NSURLSessionDataTask to complete before continuing code

在此代码中,request.getStatusCode()request.getIsConnected() 都在 task 块中的代码执行之前执行

即dataTask.resume() 在执行任何后续代码之前不会执行,无论是同一函数 class 中的代码还是单独的 class 中的代码。

我试过将函数调用放在主队列(串行)、全局队列(并发)、手动串行队列、手动并发队列和 NSOperationQueue 中,然后是一个 while 循环等待直到关闭完成。

while isDoingSomething {
    NSThread.sleepForTimeInterval(0.0)
}

循环。

我已将所有 GCD 或操作排除在这段代码之外,以避免我尝试过的每个队列场景的混乱。

ViewController.swift

import Cocoa
class ViewController: NSViewController {
    .
    .
    .
    func login(username username: String, password: String) {
        let url = "https://www.awebsite.com/login"
        let URL = NSURL(string: url)
        let method = "POST"
        let params = "username=\(username)&password=\(password)"
        vis = URLVisitor(URL: URL!, params: params, method: method, jsonParams: [:])
        vis.execute()
        cookies = vis.getCookies()
        let contentsOfURL = vis.getContentsOfURL()
    }
    .
    .
    .
}

URLVisitor.swift

import Cocoa
let queue = NSOperationQueue

class URLVisitor: NSOperation {
    .
    .
    .

    func execute() {
        let request = Request(URL: URL!, params: params, method: method, jsonParams: jsonParams)
        if !self.cookies.isEmpty {
            request._setCookies(self.cookies)
        }

        request._setAuthorizationHeader(self.authorizationHeader)
        request.sendRequest()

        self.statusCode = request.getStatusCode()
        self.isConnected = request.getIsConnected()
    }
    .
    .
    .

}

Request.swift

import Cocoa
class Request: NSOperation {
    .
    .
    .

    func sendRequest() { 
        let session = NSURLSession.sharedSession()
        let request = NSMutableURLRequest(URL: URL)
        request.HTTPMethod = method

        // send jsonParams or params
        if jsonParams.count != 0 {
            do {
                let jsonData = try NSJSONSerialization.dataWithJSONObject(jsonParams, options: .PrettyPrinted)

                request.setValue("aplication/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
                request.HTTPBody = jsonData
            } catch {   
        }
    } else {
        request.HTTPBody = self.params.dataUsingEncoding(NSUTF8StringEncoding)
    }
    let task = session.dataTaskWithRequest(request) {
        (data, response, error) in

    NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookies(self.cookies, forURL: self.URL, mainDocumentURL: nil)
        if data != nil {
            self.data = data!
            do {
                let responseHeaders = response as! NSHTTPURLResponse
                self.statusCode = responseHeaders.statusCode

                switch self.statusCode {
                    case 200:
                        print("200: OK. getting contentsOfURL and cookies")
                        self.contentsOfURL = try NSString(contentsOfURL: self.URL, encoding: NSUTF8StringEncoding)
                        self.cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookiesForURL(self.URL)!

                    case 400:
                        print("400: page not found on web")

                    case 404:
                        print("404: page not found on server")

                    case 407:
                        print("407: failed authenticate proxy credentials")

                    default:
                        print("unable to get statusCode")
                }
            } catch {

            }
        } else {
            print("\(self.statusCode): unable to get response ")
        }   
        NSThread.sleepForTimeInterval(1.0)
    }
    task.resume()
}

正确的方法是添加完成处理程序

func sendRequest(completion: () -> Void) {
    // ...
    let task = session.dataTaskWithRequest(request) {
        // ...

        completion()
    }
    task.resume()
}

用法:

let r = Request()
r.sendRequest {
    // It's done, do something
}

如果你非要阻塞线程(希望不是主线程),使用信号量。但是请记住,无论请求成功还是失败,都要向信号量发出信号。我见过太多在请求失败时忘记发出信号量的代码,因此应用程序挂断了。

func sendRequest() {
    let semaphore = dispatch_semaphore_create(0)

    let task = session.dataTaskWithRequest(request) {
        // ...
        dispatch_semaphore_signal(semaphore)
    }
    task.resume()
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
}