【问题标题】:Where is dataTaskWithRequest method used?dataTaskWithRequest 方法在哪里使用?
【发布时间】:2016-11-02 18:47:01
【问题描述】:
public func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDataTask

我知道上面的使用方法。

public func dataTaskWithRequest(request: NSURLRequest) -> NSURLSessionDataTask 

但是这个方法有什么用呢?我对它的使用方式感到困惑。我看到的所有示例都使用第一种方法。该方法没有返回数据,没有要处理的错误,也没有任何响应?还是您以某种方式将其封装为一项任务,然后在队列中运行?

【问题讨论】:

    标签: swift nsurlsession


    【解决方案1】:

    如果您为自定义NSURLSession 指定了委托,则使用后一种方法。数据不会返回到闭包,而是会话调用您的委托的didReceiveData,您必须单独实现。

    但是,实现委托方法需要更多的工作,因此您通常只在绝对必须执行的情况下执行该操作(例如,您希望在数据进入时对其进行处理,而不是等待所有数据进入;您需要委托方法来自定义处理重定向和挑战;您正在做后台NSURLSession 下载或上传任务而不是数据任务;等等)。


    例如,要发出一个简单的 GET 请求,期望 JSON 响应,您可以定义您的类以符合 URLSessionDelegateURLSessionDataDelegate 和可选的 URLSessionTaskDelegate(称为 NSURLSessionDelegateNSURLSessionDataDelegateNSURLSessionTaskDelegate,分别在 Objective-C 和 Swift 2 中),然后在 Swift 3 中执行如下操作:

    var responseData: Data?
    
    func performRequest() {
        let url = URL(string: "http://ip.jsontest.com/")!
        var request = URLRequest(url: url)
        request.setValue("application/json", forHTTPHeaderField: "Accept")
        let configuration = URLSessionConfiguration.default
        let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
        responseData = Data()
        let task = session.dataTask(with: request)
        task.resume()
    }
    
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        responseData!.append(data)
    }
    
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        guard error == nil else {
            print(error!)
            return
        }
    
        do {
            guard let jsonObject = try JSONSerialization.jsonObject(with: responseData!) as? [String: AnyObject] else {
                print("response was not JSON dictionary")
                print("responseString: \(String(data: responseData!, encoding: .utf8))")
                return
            }
    
            print(jsonObject)
        } catch let parseError {
            print("JSON Error: \(parseError)")
        }
    }
    

    或者,在 Swift 2 中:

    var responseData: NSMutableData?
    
    func performRequest() {
        let url = NSURL(string: "http://ip.jsontest.com/")!
        let request = NSMutableURLRequest(URL: url)
        request.setValue("application/json", forHTTPHeaderField: "Accept")
        let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
        let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
        responseData = NSMutableData()
        let task = session.dataTaskWithRequest(request)
        task.resume()
    }
    
    func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
        responseData!.appendData(data)
    }
    
    func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
        guard error == nil else {
            print(error!)
            return
        }
    
        do {
            guard let jsonObject = try NSJSONSerialization.JSONObjectWithData(responseData!, options: []) as? [String: AnyObject] else {
                print("response was not JSON dictionary")
                print("responseString: \(String(data: responseData!, encoding: NSUTF8StringEncoding))")
                return
            }
    
            print(jsonObject)
        } catch let parseError {
            print("JSON Error: \(parseError)")
        }
    }
    

    显然,除非您也需要一些更复杂的委托方法,否则您不会这样做,但我想展示一个极简实现(这有助于我们欣赏完成处理程序的再现)。

    【讨论】:

    • 1) 实时流是否是“处理传入的数据”的一个很好的例子? 2) 在一个班级中有多个任务会有点困难吗?我的意思是你必须在每个委托方法中继续使用 if 语句。 3) DidReceiveData 方法基本上只是消耗任务当它进来时didCompleteWithError 是为了做什么之后完成了吗? 4)“你想处理传入的数据”DidReceiveData 中对吗?
    • 1.是的,流媒体协议就是一个很好的例子,尽管我们承认我们不必经常这样做。 2. 在单个类中处理多个请求并不难,但无可否认,这比完成处理程序模式要麻烦得多。 3. 是的,这是一个常见的模式,在didReceiveData 中捕获数据(附加到Data 对象或流到OutputStream),并在didComplete 中启动后处理。 4. 是的。
    • 再次感谢5)我很困惑为什么你有一个警卫一个接球。为什么需要 2 层错误处理?
    • 我其实有三个级别。 error 对象的 guard 是为了确保没有基本的网络错误(例如,没有互联网连接等)。 NSJSONSerializationguard 是为了以防万一 Web 服务器响应的 JSON 不是字典(这极不可能,也不是我们经常担心的错误)。 catch 是为了以防响应根本不是 JSON(例如,Web 服务器报告了一些错误,例如 404 - 找不到资源)。所以第一个和第三个错误是我们无法控制的,绝对应该检查。第二个取决于你。
    【解决方案2】:
    public func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDataTask
    

    在完成处理程序中直接返回 dataresponseerror


    public func dataTaskWithRequest(request: NSURLRequest) -> NSURLSessionDataTask 
    

    URLSessionDataDelegate 协议结合使用。需要实现几个方法。在这几个阶段调用委托方法来处理数据。

    协议方法提供对加载过程的更精细控制,例如处理凭据请求和重定向控制。

    【讨论】:

    • 不使用完成处理程序有什么好处?