iOS NSURLSession 使用自定义委托处理数据任务的完成

Posted

技术标签:

【中文标题】iOS NSURLSession 使用自定义委托处理数据任务的完成【英文标题】:iOS NSURLSession handle the completion of a data task with a custom delegate 【发布时间】:2015-12-07 23:25:35 【问题描述】:

我的最终目标是在后台发出 http 请求,同时在内存中处理响应。据我了解,后台请求必须使用自定义委托(这意味着我不能使用dataTaskWithRequest(request, completionHandler)),并且要处理内存中的响应,我必须使用数据任务(这意味着我不能使用一个下载任务以及URLSession(session, downloadTask, didFinishDownloadingToURL))。

据此:https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/NSURLSessionConcepts/NSURLSessionConcepts.html#//apple_ref/doc/uid/10000165i-CH2-SW1 看起来没有任何委托方法会在完成后为数据任务调用。是通过委托处理响应以通过URLSession(session, dataTask, data) 处理单个 NSData 片段的唯一方法吗? https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSessionDataDelegate_protocol/index.html#//apple_ref/occ/intfm/NSURLSessionDataDelegate/URLSession:dataTask:didReceiveData:没有委托方法将整个最终响应作为单个 NSData 实例处理?

【问题讨论】:

【参考方案1】:

对于 API 调用等少量数据或要立即显示的头像等小图像,dataTaskWithRequest(request, completionHandler) 是您想要的方法。它设置了一个异步任务,这意味着当您启动任务时,执行将立即返回到您的代码,并且该任务将在您的应用程序运行时“在后台”下载数据并将其缓冲在内存中。一旦所有的下载任务完成,它会调用你的 completionHandler 让它知道它已经完成并且数据已经准备好了。它会将数据作为参数传递给您的处理程序。

对于播客、视频和大图片等较大的文件,即使用户开始查看其他应用并且您的应用已暂停,您也会希望 iOS 为您下载文件。然后,您将需要使用 NSURLSessionDownloadTask 和后台会话配置 backgroundSessionConfigurationWithIdentifier: 和自定义委托。您的自定义委托将需要实现方法URLSession:downloadTask:didFinishDownloadingToURL:。调用此方法时,您可以读取url 处的文件内容,该文件将使用如下代码传递给您:

let data = NSData(contentsOfURL: url)

iOS 应用程序退出后仍然存在后台下载的原因是这样处理的,因为 iOS 希望能够代表不同的应用程序(如播客、视频等)继续下载多个文件。如果用户处于高位高速网络,在内存中下载多个大文件会很快消耗所有设备的内存,因此它们在下载时被存储。同样,在使用NSData(contentsOfURL:) 将整个文件读入内存之前,您应该牢记文件大小。

这是一个工作示例,说明所有内容如何组合在一起。将此粘贴到 iOS Playground 并查看您将获得的图像:

import UIKit

class MyDelegate: NSObject, NSURLSessionDelegate, NSURLSessionDownloadDelegate 
  func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) 
    if let data = NSData(contentsOfURL: location) 
      // work with data ...
      UIImage(data: data)
    
  


let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("my-session-identifier")
let delegate = MyDelegate()
let session = NSURLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
let url = NSURL(string: "https://pbs.twimg.com/profile_images/473550724433858560/tuHsaI2U.png")!
let task = session.downloadTaskWithURL(url)
task.resume()

// this allows the code to run in a playground
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

【讨论】:

Apple 的文档给我的印象是,如果您想在后台处理响应,则不能使用 dataTaskWithRequest(request, completionHandler),您必须使用通过您的应用程序实例化的 NSURLSessionDelegate生命周期通过一些复杂的过程(甚至还没有到达那里......)developer.apple.com/library/ios/documentation/Cocoa/Conceptual/… @user1084447 一开始我误解了你在后台的意思。我已经扩展了解决 iOS 应用程序后台问题的答案。希望这能回答它! @hashemi 不需要使用 NSData。你可以使用 UIImage(contentsOfFile: location.path!) @LeoDabus 正确。 OP 想要 NSData 所以我不想跳过这一步。 UIImage 是肉汁。 谢谢@hashemi。我想尝试将响应保留在内存中的原因是因为 1)我正在调用我们自己的 api 并控制各个响应大小,并确保它们保持较小;并且 2) 我将解析响应并更新持久性 SQLite 存储,因此从磁盘读取文件只是为了更新磁盘上的 SQLite 存储对我来说听起来像是浪费 io...我发现另一篇文档证实了使用数据任务执行此操作的唯一方法是手动处理片段(仅在 ios8+ 中可能)。我将尝试两种方法并进行比较。【参考方案2】:

如果我正确理解了您应该实施的文档

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

来自URLSessionTaskDelegate 的方法。它“告诉代表任务完成了数据传输”(根据文档)。 它将使用errornil 值调用,但您还必须检查task.response 是否存在潜在问题。

在此之前,您应该手动收集来自URLSession:dataTask:didReceiveData: 调用的数据片段:

此委托方法可能会被多次调用,并且每次调用仅提供自上次调用以来接收到的数据。如果需要,应用程序负责累积这些数据。

没有可以为您将所有数据组合到一个对象中的委托方法,您必须手动完成。

【讨论】:

以上是关于iOS NSURLSession 使用自定义委托处理数据任务的完成的主要内容,如果未能解决你的问题,请参考以下文章

如何将 cookie 传递给 NSUrlSession?

我们可以为 NSURLSessionTaskDelegate 设置一个自定义委托对象吗

闭包内的 Swift NSURLSession 访问委托

iOS NSURLSession 监听超时

不调用 NSURLSession 委托方法

iOS如何知道使用自定义委托按下哪个按钮CollectionView