NSURLSession代理未被调用。

22
在以下代码中,文件可以成功下载。然而,似乎没有调用任何委托方法,因为我没有接收到任何输出。进度视图也没有更新。有什么想法吗?
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]传递给delegateQueue参数,而不是nil。 - rdelmar
3个回答

34
从我的测试来看,你必须选择使用代理(delegate)还是完成处理程序(completion handler) - 如果同时指定两者,只有完成处理程序会被调用。这段代码提供了运行进度更新和 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文档中,这是相关部分:

像大多数网络API一样,NSURLSession API高度异步。根据您调用的方法,它以以下两种方式之一返回数据:

  • 通过完成处理程序块将数据返回给应用程序,当传输成功或出现错误时使用。
  • 通过在接收数据时调用自定义委托上的方法。
  • 通过在下载文件完成时调用自定义委托上的方法。
因此,根据设计,它会将数据返回给 要么 完成处理程序块 要么 委托。但正如此处所证明的那样,不会同时返回。

我不确定那是否正确,否则那将是可怕的API设计。此外,请看一下AFNetworking,AFURLSessionManager基于委托回调构建了所有设计,但您仍然可以使用完成处理程序创建请求... - Daniel Galasko
2
这只是一个有趣的API细节,处理程序会覆盖委托:) - Daniel Galasko
2
“有趣”是其中一种说法!已经苦恼了很长时间。为什么我不能同时使用完成处理程序,并通过委托管理缓存行为,例如?看起来很奇怪。 - stephent
谢谢Nate!我已经尝试了一个小时来获取委托方法的响应 - 但我也在使用完成处理程序。谢谢!! - adamteale
1
我可以确认,completionHandler确实会抑制代理。另外,不要忘记实现URLSession:task:didCompleteWithError:方法,以便您也能接收到任何错误!(完成处理程序提供了一个NSError *error参数)。 - EricWasTaken
显示剩余4条评论

20

有趣的是,在他们的NSURLSessionDataDelegate中,苹果公司特别解释了这种行为(但在基本代理NSURLSessionTaskDelegateNSURLSessionDownloadDelegate中未解释)。

注意

一个NSURLSession对象不一定需要一个代理。如果没有分配代理,则在该会话中创建任务时,必须提供完成处理程序块以获取数据。

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


1
但是对于上传任务,即使提供了完成块,委托也会被调用! - Michał Ziobro

3

Swift 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")
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接