URL会话的生命周期(Life Cycle of a URL Session)
您可以通过两种方式使用NSURLSession
API:使用系统提供的代理或使用您自己的代理。一般来说,如果您的应用程序执行以下任何操作,您必须使用自己的代理:
- 在应用程式未执行时,使用后台会话下载或上传内容。
- 执行自定义身份验证。
- 执行自定义SSL证书验证。
- 决定是将传输下载到磁盘还是基于服务器返回的MIME类型或其他类似条件显示。
- 从主体流(而不是NSData对象)上传数据。
- 以编程方式限制缓存。
- 以编程方式限制HTTP重定向。
如果您的应用程序不需要执行任何这些操作,您的应用程序可以使用系统提供的代理。根据您选择的技术,您应该阅读以下部分之一:
-
具有系统提供的代理的URL会话的生命周期(Life Cycle of a URL Session with System-Provided Delegates)
提供了一个轻量描述,您的代码如何创建和使用URL会话。即使您打算编写自己的代理,也应该阅读本部分,因为它可以让您全面了解代码必须做什么才能配置对象并使用它。 -
具有自定义代理的URL会话的生命周期(Life Cycle of a URL Session with Custom Delegates)
提供了URL会话操作中的每个步骤的完整描述。您应参考本节来帮助您了解会话如何与其代理进行交互。特别地,这说明了每个代理方法被调用的时机。
具有系统提供的代理的URL会话的生命周期(Life Cycle of a URL Session with System-Provided Delegates)
如果您使用NSURLSession
类而不提供代理对象,则系统提供的代理会处理您的许多详细信息。以下是当您使用NSURLSession
和系统提供的代理时,您的应用程序必须进行的方法调用和完成处理程序(completion handler)调用的基本顺序:
创建会话配置。对于后台会话,此配置必须包含唯一标识符。存储该标识符,如果您的应用程序崩溃或已终止或暂停(suspended),则使用该标识符与会话重新关联。
创建一个会话,指定一个配置对象和一个nil代理。
在会话中创建每个都表示资源请求的任务对象。
每个任务从暂停(suspended)状态开始。在您的应用程序调用恢复(resume)任务后,它开始下载指定的资源。
任务对象是NSURLSessionTask
的子类-NSURLSessionDataTask
,NSURLSessionUploadTask
或NSURLSessionDownloadTask
,具体取决于您尝试实现的行为。
虽然您的应用程序可以(通常应该)向会话中添加多个任务,但是为了简单起见,剩余的步骤描述了单个任务的生命周期。
重要:如果您使用
NSURLSession
类而不提供代理,则您的应用程序必须使用接受completionHandler
参数的调用创建任务,否则无法从类中获取数据。
对于下载任务,在从服务器传输期间,如果用户通知您的应用程序暂停下载,请通过调用
cancelByProducingResumeData:
方法来取消该任务。稍后,将返回的恢复数据传递给downloadTaskWithResumeData:
或downloadTaskWithResumeData:completionHandler:
方法,以创建一个新的下载任务,继续下载。当任务完成时,
NSURLSession
对象调用任务的完成处理程序(completion handler)。
注意:
NSURLSession
不通过error
参数报告服务器错误。您的应用通过error
参数接收的唯一错误是客户端错误,例如无法解析主机名或连接到主机。错误代码在URL加载系统错误代码(URL Loading System Error Codes)
中描述。服务器端错误通过
NSHTTPURLResponse
对象中的HTTP状态代码报告。有关更多信息,请阅读NSHTTPURLResponse
和NSURLResponse
类的文档。
- 当您的应用程序不再需要会话时,通过调用
invalidateAndCancel
(取消未完成的任务)或finishTasksAndInvalidate
(允许未完成的任务完成,然后使对象失效)使其失效。
具有自定义代理的URL会话的生命周期(Life Cycle of a URL Session with Custom Delegates)
您可以经常使用NSURLSession
API无需提供代理。但是,如果您使用NSURLSession
API进行后台下载和上传,或者如果需要以非默认方式处理身份验证或缓存,则必须提供符合「会话代理协议,一个或多个任务代理协议或这些协议的某些组合」的代理。这个代理有很多用途:
- 与下载任务一起使用时,
NSURLSession
对象使用代理为应用程序提供文件URL,以便获取下载的数据。 - 所有后台下载和上传都需要代理。这些代理必须提供
NSURLSessionDownloadDelegate
协议中的所有代理方法。 - 代理可以处理某些身份验证质询(authentication challenges)。
- 代理提供主体流,用于将基于流的数据上传到远程服务器。
- 代理可以决定是否遵循HTTP重定向。
-
NSURLSession
对象使用代理为应用程序提供每个传输的状态。数据任务代理接收初始调用,在其中您可以将请求转换为下载和随后的调用,提供从远程服务器到达的数据。 - 代理是
NSURLSession
对象可以在传输完成时告诉您的应用程序的一种方式。
如果您使用自定义代理,则URL会话的完整生命周期更复杂。以下是应用程序必须的方法和使用具有自定义代理的NSURLSession时应用程序接收的代理调用的基本顺序:
创建会话配置。对于后台会话,此配置必须包含唯一标识符。存储该标识符,如果您的应用程序崩溃或已终止或暂停,则使用该标识符与会话重新关联。
创建会话,指定配置对象和可选的代理。
在会话中创建每个都表示资源请求的任务对象。
每个任务从暂停(suspended)状态开始。在您的应用程序调用恢复(resume)任务后,它开始下载指定的资源。
任务对象是NSURLSessionTask
的子类-NSURLSessionDataTask
,NSURLSessionUploadTask
或NSURLSessionDownloadTask
,具体取决于您尝试实现的行为。
虽然您的应用程序可以(通常应该)向会话中添加多个任务,但是为了简单起见,剩余的步骤描述了单个任务的生命周期。
- 如果远程服务器返回指示需要身份验证的状态代码,并且如果该身份验证需要连接级别质询(例如SSL客户端证书),则
NSURLSession
将调用认证质询代理方法。
对于会话级别质询(challenges) -
NSURLAuthenticationMethodNTLM
,NSURLAuthenticationMethodNegotiate
,NSURLAuthenticationMethodClientCertificate
或NSURLAuthenticationMethodServerTrust
-NSURLSession
对象调用会话代理的URLSession:didReceiveChallenge:completionHandler:
方法。如果您的应用程序没有提供会话代理方法,NSURLSession
对象将调用任务代理的URLSession:task:didReceiveChallenge:completionHandler:
方法来处理质询。对于非会话级质询(所有其他),
NSURLSession
对象调用任务代理的URLSession:task:didReceiveChallenge:completionHandler:
方法来处理质询。如果您的应用程序提供了一个会话代理,并且您需要处理身份验证,那么您必须在任务级别处理身份验证,或者提供显式调用每会话(per-session)处理程序的任务级处理程序。会话代理的URLSession:didReceiveChallenge:completionHandler:
方法不是为非会话级调用的。
注意:Kerberos身份验证是透明处理的。
如果上传任务的身份验证失败,那么如果任务的数据是从流提供的,NSURLSession
对象将调用代理的URLSession:task:needNewBodyStream:
代理方法。然后,代理必须提供一个新的NSInputStream
对象,以提供新请求的主体数据。
有关为NSURLSession
编写身份验证代理方法的详细信息,请阅读身份验证质询和TLS链验证(Authentication Challenges and TLS Chain Validation)
。
- 收到HTTP重定向响应时,
NSURLSession
对象调用代理的URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:
方法。该代理方法使用提供的NSURLRequest对象(遵循重定向)调用提供的完成处理程序,新的NSURLRequest对象(重定向到其他网址)或nil(将重定向的响应主体视为有效响应并将其作为结果返回)。
如果遵循重定向,请返回到步骤4(身份验证质询处理)。
如果代理没有实现此方法,重定向会跟随到最大重定向数。
对于通过调用
downloadTaskWithResumeData:
或downloadTaskWithResumeData:completionHandler:
创建的(重新)下载任务,NSURLSession
调用代理的URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:
方法,新的任务对象作为参数。对于数据任务,
NSURLSession
对象调用代理的URLSession:dataTask:didReceiveResponse:completionHandler:
方法。决定是否将数据任务转换为下载任务,然后调用完成回调以继续接收数据或下载数据。
如果您的应用程序选择将数据任务转换为下载任务,NSURLSession
会调用代理的URLSession:dataTask:didBecomeDownloadTask:
方法,并将新的下载任务作为参数。在此调用之后,代理不从数据任务接收进一步的回调,并开始从下载任务接收回调。
如果任务是使用
uploadTaskWithStreamedRequest:
创建的,那么NSURLSession
会调用代理的URLSession:task:needNewBodyStream:
方法来提供主体数据。在将主体内容初始上传到服务器(如果适用)期间,代理定期接收
URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
报告上传进度的回调。在从服务器传输期间,任务代理定期接收回调以报告传输进度。对于下载任务,会话调用代理的
URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:
方法,字节数已成功写入磁盘。对于数据任务,会话在接收到实际的数据片段时调用代理的URLSession:dataTask:didReceiveData:
方法。
对于下载任务,在从服务器传输期间,如果用户告诉您的应用程序暂停下载,请通过调用cancelByProducingResumeData:
方法来取消该任务。
稍后,如果用户要求您的应用程序恢复下载,请将返回的恢复数据传递给downloadTaskWithResumeData:
或downloadTaskWithResumeData:completionHandler:
方法以创建一个继续下载的新下载任务,然后转到步骤3(创建和恢复任务 对象)。
对于数据任务,
NSURLSession
对象调用代理的URLSession:dataTask:willCacheResponse:completionHandler:
方法。您的应用程序应该决定是否允许缓存。如果不实现此方法,则默认行为是使用会话配置对象中指定的缓存策略。如果下载任务成功完成,则
NSURLSession
对象调用任务的URLSession:downloadTask:didFinishDownloadingToURL:
方法,其中包含临时文件的位置。在此代理方法返回之前,您的应用程序必须读取此文件中的响应数据或将其移动到应用程序沙盒容器目录中的永久位置。当任何任务完成时,
NSURLSession
对象调用代理的URLSession:task:didCompleteWithError:
方法,一个错误对象或nil(如果任务成功完成)作为参数。
如果任务失败,大多数应用程序应重试请求,直到用户取消下载或服务器返回一个错误,指示该请求永远不会成功。不过,您的应用程式不应立即重试。相反,它应该使用可达性(reachability)API来确定服务器是否可达,并且只有当它接收到可达性已更改的通知时才应该发出新请求。
如果可以恢复下载任务,NSError
对象的userInfo
字典包含NSURLSessionDownloadTaskResumeData
键的值。您的应用程序应该传递此值以调用downloadTaskWithResumeData:
或downloadTaskWithResumeData:completionHandler:
创建一个新的下载任务,继续现有的下载。
如果无法恢复任务,您的应用程序应创建一个新的下载任务,并从头重新启动事务。
在任一情况下,如果由于服务器错误以外的任何原因传输失败,请转到步骤3(创建和恢复任务对象)。
注意:
NSURLSession
不通过error
参数报告服务器错误。代理通过error
参数接收的唯一错误是客户端错误,例如无法解析主机名或连接到主机。错误代码在URL加载系统错误代码中描述。服务器端错误通过
NSHTTPURLResponse
对象中的HTTP状态代码报告。
有关更多信息,请阅读NSHTTPURLResponse
和NSURLResponse
类的文档。
如果响应是多部分编码的,则会话可以再次调用代理的
didReceiveResponse
方法,然后再执行零个或多个额外的didReceiveData
调用。如果发生这种情况,请转到步骤7(处理didReceiveResponse
调用)。当您不再需要会话时,通过调用
invalidateAndCancel
(取消未完成的任务)或finishTasksAndInvalidate
(允许未完成的任务完成,然后使对象失效)使其无效。
在使会话无效后,当所有未完成的任务已被取消或已完成时,会话向代理发送URLSession:didBecomeInvalidWithError:
消息。
当该代理方法返回时,会话将其强引用返回给代理(释放对代理的强引用)。
要点:会话对象保持对代理的强引用,直到应用程序显式使会话无效。如果你不使会话无效,你的应用程序泄漏内存。
如果您的应用程序取消正在进行的下载,NSURLSession
对象将调用代理的URLSession:task:didCompleteWithError:
方法,就像发生错误一样。