NSURLSession 委托未调用

Posted

技术标签:

【中文标题】NSURLSession 委托未调用【英文标题】:NSURLSession delegates not called 【发布时间】:2014-11-02 00:10:49 【问题描述】:

在下面的代码中,文件下载得很好。但是,似乎没有调用任何委托方法,因为我没有收到任何输出。 progressView 也没有更新。知道为什么吗?

import Foundation
import UIKit

class Podcast: PFQueryTableViewController, UINavigationControllerDelegate, MWFeedParserDelegate, UITableViewDataSource, NSURLSessionDelegate, NSURLSessionDownloadDelegate 

    func downloadEpisodeWithFeedItem(episodeURL: NSURL) 

    var request: NSURLRequest = NSURLRequest(URL: episodeURL)
    let config = NSURLSessionConfiguration.defaultSessionConfiguration()
    let session = NSURLSession(configuration: config, delegate: self, delegateQueue: nil)

    var downloadTask = session.downloadTaskWithURL(episodeURL, completionHandler:  (url, response, error) -> Void in
        println("task completed")
        if (error != nil) 
            println(error.localizedDescription)
         else 
            println("no error")
            println(response)
        
    )
    downloadTask.resume()



func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) 
    println("didResumeAtOffset")


    func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) 
          var downloadProgress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
    println(Float(downloadProgress))
    println("sup")

    epCell.progressView.progress = Float(downloadProgress)


     func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) 
    println(location)



【问题讨论】:

尝试将 [NSOperationQueue mainQueue] 而不是 nil 传递给 delegateQueue 参数 【参考方案1】:

根据我的测试,您必须选择是要使用委托还是完成处理程序 - 如果您同时指定两者,则只会调用完成处理程序。这段代码给了我运行进度更新和didFinishDownloadingToURL 事件:

func downloadEpisodeWithFeedItem(episodeURL: NSURL) 
    let request: NSURLRequest = NSURLRequest(URL: episodeURL)
    let config = NSURLSessionConfiguration.defaultSessionConfiguration()
    let session = NSURLSession(configuration: config, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
    
    let downloadTask = session.downloadTaskWithURL(episodeURL)
    downloadTask.resume()


func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) 
    println("didResumeAtOffset: \(fileOffset)")


func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) 
    var downloadProgress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
    println("downloadProgress: \(downloadProgress)")


func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) 
    println("didFinishDownloadingToURL: \(location)")
    println(downloadTask)


来自NSURLSession documentation,以下是相关部分:

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

到完成处理程序块,当传输成功完成或出现错误时将数据返回到您的应用程序。 在收到数据时调用自定义委托上的方法。 下载到文件完成后调用自定义委托上的方法。

因此,根据设计,它会将数据返回给完成处理程序块委托。但正如这里所证明的,不是两者兼而有之。

【讨论】:

我不确定这是否正确,否则那将是可怕的 API 设计。此外看看 AFNetworking,AFURLSessionManager 的所有设计都基于委托回调,但您仍然可以使用完成处理程序创建请求... 这可能是可怕的 API 设计,也可能是一个错误,因为该行为未记录在案。 AFNetworking 早于NSURLSession - 它基于NSURLConnectionNSURLSession 旨在取代。在任何情况下,持久化会话对象都不能解决问题。 它只是一个有趣的 API 细节,处理程序覆盖了委托:) “有趣”是一种表达方式!为这个困惑太久了。为什么我不能使用完成处理程序,例如,同时通过委托管理缓存行为?看起来很奇怪。 我可以确认 completionHandler 确实抑制了代表。另外,不要忘记实现URLSession:task:didCompleteWithError:,这样您也会收到任何错误! (完成处理程序为您提供NSError *error 参数)。【参考方案2】:

有趣的是,Apple specifically explains this behavior in their NSURLSessionDataDelegate(但既不在基础委托 NSURLSessionTaskDelegate 也不在 NSURLSessionDownloadDelegate 中)

注意

NSURLSession 对象不需要有委托。如果没有分配委托,当您在该会话中创建任务时,您必须提供完成处理程序块来获取数据。

完成处理程序块主要用作使用自定义委托的替代方案。 如果您使用采用完成处理程序块的方法创建任务,则不会调用用于响应和数据传递的委托方法。

【讨论】:

但是即使提供了完成块,也会调用上传任务委托!【参考方案3】:

斯威夫特 3

class ViewController: UIViewController 
    var urlLink: URL!
    var defaultSession: URLSession!
    var downloadTask: URLSessionDownloadTask!


// MARK: Button Pressed
    @IBAction func btnDownloadPressed(_ sender: UIButton) 
        let urlLink1 = URL.init(string: "https://github.com/VivekVithlani/QRCodeReader/archive/master.zip")
        startDownloading(url: urlLink!)

    @IBAction func btnResumePressed(_ sender: UIButton) 
    downloadTask.resume()


@IBAction func btnStopPressed(_ sender: UIButton) 
    downloadTask.cancel()


@IBAction func btnPausePressed(_ sender: UIButton) 
    downloadTask.suspend()


    func startDownloading (url:URL) 
        let backgroundSessionConfiguration = URLSessionConfiguration.background(withIdentifier: "backgroundSession")
        defaultSession = Foundation.URLSession(configuration: backgroundSessionConfiguration, delegate: self, delegateQueue: OperationQueue.main)
        downloadProgress.setProgress(0.0, animated: false)
        downloadTask = defaultSession.downloadTask(with: urlLink)
        downloadTask.resume()
    

// MARK:- URLSessionDownloadDelegate
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) 
    print("File download succesfully")


func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) 
    downloadProgress.setProgress(Float(totalBytesWritten)/Float(totalBytesExpectedToWrite), animated: true)


func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) 
    downloadTask = nil
    downloadProgress.setProgress(0.0, animated: true)
    if (error != nil) 
        print("didCompleteWithError \(error?.localizedDescription)")
    
    else 
        print("The task finished successfully")
    

【讨论】:

以上是关于NSURLSession 委托未调用的主要内容,如果未能解决你的问题,请参考以下文章

NSURLSession:后台崩溃,因为没有调用上传委托

应该如何处理 NSURLAuthenticationMethodXMobileMeAuthToken?

NSURLSession 未下载完整数据,但成功完成

AFHTTPSessionManager 未解除分配

URLSessionDidFinishEventsForBackgroundURLSession 未调用-Objective-C

从 Swift 命令行程序中使用 NSURLSession