NSURLSessionTaskDelegate 方法 URLSession:task:didCompleteWithError: 从不调用

Posted

技术标签:

【中文标题】NSURLSessionTaskDelegate 方法 URLSession:task:didCompleteWithError: 从不调用【英文标题】:NSURLSessionTaskDelegate method URLSession:task:didCompleteWithError: never called 【发布时间】:2014-11-17 23:58:33 【问题描述】:

我所有的网络代码都依赖于 NSURLSession 委托方法——而不是完成处理程序。我的数据和下载任务都很好,但我的上传任务从来没有导致 URLSession:task:didCompleteWithError: 被调用。然而 URLSession:dataTask:didReceiveData:URLSession:dataTask:willCacheResponse:completionHandler: 委托方法 ARE 被调用。

如果我在会话对象上将资源超时设置为非常低的值,那么didCompleteWithErrors 会被调用,但这显然不是解决方案。

有什么想法吗?我快要疯了。

谢谢。

【问题讨论】:

嗨@mph 你是如何设置这个任务委托的?实际上我遇到了一些问题,这些代表没有被调用。 【参考方案1】:

如果您实现willCacheResponse,您将不会看到调用didCompleteWithError,但如果实现未能实际调用completionHandler。您必须拨打completionHandler

所有提供completionHandler 参数的各种NSURLSession 委托方法(例如身份验证质询、重定向等)也是如此。如果你实现了这些各自的方法,你必须确保completionHandler被调用。

【讨论】:

谢谢。我知道它一定是像这样微不足道的事情。 发布我的问题后,我注意到我的一些数据和下载任务也遇到了同样的问题。我猜简短而甜蜜的单元测试并不总是遇到同样的问题,这实际上使它成为一个更令人讨厌和(看似)无证的问题。啊。 在为 Apple 的文档辩护时,它说,“如果你的委托实现了这个方法,它必须调用这个完成处理程序”(强调来自原始文档)。它并不能准确地说明如果你不调用 completionHandler 会发生什么(更糟糕的是,文档描述了会导致泄漏,但忽略了承认可能出现的更广泛的问题),但在他们的辩护中,他们是公平的明确必须调用完成处理程序。但我能感受到你的痛苦:我知道这个问题的唯一原因是我自己曾经经历过。【参考方案2】:

Rob 的回答是正确的,给了我一个很大的线索。但是 - 对于我的具体问题 - 为什么可能不会调用 NSURLSessionTaskDelegate 方法并没有引起我的注意。如果可以允许我提供另一个重点不同的答案。

许多NSURLSession 任务创建方法有两种变体;一个带有completionHandler 参数,一个没有。

例如:

func dataTaskWithRequest(_ request: NSURLRequest) -> NSURLSessionDataTask

func dataTaskWithRequest(_ request: NSURLRequest, completionHandler completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDataTask

如果使用completionHandler 方法,那么任何委托方法——在这种情况下为NSURLSessionTaskDelegate将被忽略。尽管在 NSURLSession 初始化程序中指定了委托。

以下 Playground 代码演示了这一点。切换 task 声明 cmets 以查看是否调用了委托:

import UIKit
import XCPlayground

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true


class MyDelegate:NSObject, NSURLSessionTaskDelegate

    func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) 
        NSLog("Delegate called")
    



let myDel = MyDelegate()
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config, delegate: myDel, delegateQueue: NSOperationQueue())
let url = NSURL(string: "http://httpbin.org")
let request: NSURLRequest = NSURLRequest(URL: url!)

// With completionHandler, delegate method above NOT called
let task : NSURLSessionDataTask = session.dataTaskWithRequest(request, completionHandler: (data, response, error) in
        NSLog("Task done")
)

// Without completionHandler, delegate method above IS called
//let task : NSURLSessionDataTask = session.dataTaskWithRequest(request)

task.resume()

TLDR: 想知道为什么不调用NSURLSession 的委托方法?使用替代方法签名来创建您的会话任务; 省略了completionHandler 参数

【讨论】:

完成处理程序块没有对委托方法中使用的(NSURLSessionTask *)task 参数的引用。如果没有委托的task,如何在块内重试任务? 这个答案正确的,顺便说一句...`uploadTaskWithRequest:fromData: 命中了委托方法,但uploadTaskWithRequest:fromData:completionHandler: 没有。 就我而言,即使我没有使用 completionHandler 方法,也不会调用委托回调。【参考方案3】:

在这种情况下有人可能想要检查的另一种可能性是,您的 URLSession 委托方法未标记为私有。如果是,则不会调用方法...如果您将修饰符留在方法之外,这很容易意外发生,然后在警告上使用自动修复来修复方法的修饰符(使其私有)。

方法签名应该是:

public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) 

【讨论】:

或“open func”——今天这让我有点吃惊,感谢“正确”的答案,尽管其他问题也是完全有效的问题。放弃委托模式! @snakeoil 关于开放是有效的重要一点,只是可以看到的东西!很高兴它可以提供帮助,因为这让我困惑了一段时间。 WTF!就是这样。编译器没有警告我他们需要公开。但奇怪的是,一些委托函数被调用了。【参考方案4】:

为了在此处扩展另一个答案,NSURLSession docs 声明将调用完成处理程序委托方法。

与大多数网络 API 一样,NSURLSession API 是高度异步的。它以两种方式中的一种将数据返回到您的应用,具体取决于您调用的方法:

传输成功完成或出现错误时调用的完成处理程序块。

在接收到数据和传输完成时调用会话委托上的方法。

这里有一个更具体的问题:

NSURLSession delegate and completionHandler

【讨论】:

【参考方案5】:

我解决了这个问题,如果有的话,在 willCacheResponse 函数中添加 completionHandler

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse *__nullable cachedResponse))completionHandler 

    NSLog(@"%s", __FUNCTION__);
    completionHandler(proposedResponse);

【讨论】:

以上是关于NSURLSessionTaskDelegate 方法 URLSession:task:didCompleteWithError: 从不调用的主要内容,如果未能解决你的问题,请参考以下文章

NSURLSessionDataTask

Swift 中这些 NSURLSession 方法的代表是啥?

我当前能力对AF的理解-AFURLSessionManager.h,写给我自己

当应用程序挂起时,我如何知道 NSURLSessionUploadTask 是不是正在工作?