NSURLSession 的 task 是异步回调的,所有的回调都是在委托代理里面处理, 苹果为我们提供了 NSURLSessionDelegate 的一系列代理方法供我们选择使用,首先,我们先了解下 NSURLSession 的相关代理:
NSURLSessionDelegate继承关系.png- NSURLSessionDelegate 继承自NSObject,是 session 级别的委托,主要管理 session 的生命周期、处理证书认证等
- NSURLSessionTaskDelegate 是 task 级别的委托,面向所有的委托方法
- NSURLSessionDataDelegate 是 task 级别的委托,处理 data 和upload 的委托方法
- NSURLSessionDownloadDelegate 是 task 级别的委托,面向download的委托方法
- NSURLSessionStreamDelegate 是 task 级别的委托,面向 stream 的委托方法
这些代理都是继承关系,在NSURLSession实现中,只要设置了这个代理,它会去判断这些所有的代理,是否 respondsToSelector 这些代理中的方法,如果响应了就会去调用。
1、NSURLSessionDelegate
NSURLSession实例调用其委托来处理 session级别 事件的协议定义方法,如 session 生命周期更改。主要有三个代理方法使用:
1.1、处理 session 生命周期
1.1.1、 URLSession:didBecomeInvalidWithError:
调用时机:当前这个 session 已经失效时,该代理方法被调用;
促使 session 失效的两个方法:
finishTasksAndInvalidate : session 将等到所有 task 结束或失败后才调用这个委托方法
invalidateAndCancel : session 将直接取消所有正在执行的 task,立即调用此委托方法。
1.1.2、URLSessionDidFinishEventsForBackgroundURLSession:
告诉委托,为会话排队的所有消息都已发送。
在iOS中,当后台传输完成或需要凭据时,如果您的应用程序不再运行,您的应用程序将自动在后台重新启动,应用程序的UIApplicationDelegate将发送给 application:handleEventsForBackgroundURLSession:completionHandler: 一个信息。此调用包含导致应用程序启动的会话的标识符。然后,在创建具有相同标识符的背景配置对象之前,您应该存储该完成处理程序,并使用该配置创建一个会话。新创建的会话自动与正在进行的后台活动重新关联。
当 app 接收到 application:handleEventsForBackgroundURLSession:completionHandler: 的一个信息之后,这表明之前为该会话排队的所有消息都已交付,并且现在可以安全地调用先前存储的完成处理程序,或者开始任何可能导致调用完成处理程序的内部更新。
由于所提供的完成处理程序是UIKit的一部分,您必须在主线程上调用它。
1.2 处理 session 身份验证
URLSession:didReceiveChallenge:completionHandler:
响应远程服务器的 session 级身份验证请求;此方法在两种情况下调用:
- 当远程服务器请求客户端证书或NTLM身份验证时,调用此方法允许应用程序提供适当的凭据
- 当会话首先建立到使用SSL或TLS的远程服务器的连接时,调用此方法允许应用程序验证服务器的证书 chain
也就是说:当在 SSL 握手阶段,如果服务器要求验证客户端身份或向客户端提供其证书用于验证时,则会调用 此代理方法
如果没有实现此代理方法,session 将调用其委托的 URLSession:task:didReceiveChallenge:completionHandler: 方法代替
此方法的几个参数:
1.2.1、 challenge
NSURLAuthenticationChallenge 封装了服务端对客户端的验证请求。 根据它的 protectionSpace 属性的 authenticationMethod 可以知道服务端要通过哪种方式验证客户端或是要求客户端验证服务端的证书:
当 authenticationMethod 的值为:
NSURLAuthenticationMethodNTLM、
NSURLAuthenticationMethodNegotiate、
NSURLAuthenticationMethodClientCertificate和
NSURLAuthenticationMethodServerTrust时,
系统会先尝试调用 session 级的处理方法,若 session 级未实现,则尝试调用 task 级的处理方法,而其他情况则是直接调用 task 级的处理方法,无论 session 级方法是否实现。
关于 authenticationMethod : 接收方使用的身份验证方法
(1)、session 范围的常量
在 URLSession:didReceiveChallenge:completionHandler: 代理方法中使用
可能的常量 | 描述 |
---|---|
NSURLAuthenticationMethodClientCertificate | 验证客户端的证书 |
NSURLAuthenticationMethodNegotiate | 协商是否为这个保护空间使用Kerberos或NTLM身份验证 |
NSURLAuthenticationMethodNTLM | 使用NTLM身份验证 |
NSURLAuthenticationMethodServerTrust | 验证服务端提供的证书 |
(2)、task 范围的常量
在 URLSession:task:didReceiveChallenge:completionHandler: 代理方法中使用
可能的常量 | 描述 |
---|---|
NSURLAuthenticationMethodDefault | 默认的验证 |
NSURLAuthenticationMethodHTMLForm | 不会用于 URL Loading System,在通过 web 表单验证时可能用到 |
NSURLAuthenticationMethodHTTPBasic | 基本的 HTTP 验证,通过 NSURLCredential 对象提供用户名和密码,相当于 Default 默认的验证 |
NSURLAuthenticationMethodHTTPDigest | 类似于基本的 HTTP 验证,摘要会自动生成,同样通过 NSURLCredential 对象提供用户名和密码 |
1.2.2、 completionHandler
通知系统如何处理验证,需要传入两个参数:
- disposition 描述如何处理挑战
- credential 如果 disposition 值是NSURLSessionAuthChallengeUseCredential,则应该用于身份验证的凭据,否则返回NULL
1.2.2.1 disposition
处理身份验证的枚举 NSURLSessionAuthChallengeDisposition
枚举值 | 描述 |
---|---|
NSURLSessionAuthChallengePerformDefaultHandling | 相当于未执行代理方法,使用默认的处理方式,不使用参数 credential |
NSURLSessionAuthChallengeUseCredential | 指明通过另一个参数 credential 提供证书 |
NSURLSessionAuthChallengeCancelAuthenticationChallenge | 取消整个请求,提供的凭证参数被忽略 |
NSURLSessionAuthChallengeRejectProtectionSpace | 拒绝该 protectionSpace 的验证,不使用参数 credential |
NSURLSessionAuthChallengeRejectProtectionSpace 配置只适用于非常特殊的情况。例如,Windows服务器可能同时使用NSURLAuthenticationMethodNegotiate和NSURLAuthenticationMethodNTLM。如果 App 只能处理 NTLM,则拒绝此验证,以获得队列的NTLM挑战。
但是,大多数应用程序不会面对这种情况,如果您不能提供某种身份验证方法的凭据,您通常应该使用 NSURLSessionAuthChallengePerformDefaultHandling 配置
1.2.2.2 credential
NSURLCredential类型 ,一种身份验证凭证,包含特定于凭证类型的信息和用于使用的持久存储类型。
当 disposition 的值为 NSURLSessionAuthChallengeUseCredential 时,需要提供一个 NSURLCredential 对象。可以创建3种类型的 Credential:
- 当 protectionSpace 的 authenticationMethod 的值为 NSURLAuthenticationMethodHTTPBasic 或 NSURLAuthenticationMethodHTTPDigest 时:调用以下方法
- (instancetype)initWithUser:(NSString *)user password:(NSString *)password persistence:(NSURLCredentialPersistence)persistence;
+ (NSURLCredential *)credentialWithUser:(NSString *)user password:(NSString *)password persistence:(NSURLCredentialPersistence)persistence;
- 当 protectionSpace 的 authenticationMethod 的值为NSURLAuthenticationMethodClientCertificate 时:调用以下方法
- (instancetype)initWithIdentity:(SecIdentityRef)identity certificates:(nullable NSArray *)certArray persistence:(NSURLCredentialPersistence)persistence;
+ (NSURLCredential *)credentialWithIdentity:(SecIdentityRef)identity certificates:(nullable NSArray *)certArray persistence:(NSURLCredentialPersistence)persistence;
- 当 protectionSpace 的 authenticationMethod 的值为 NSURLAuthenticationMethodServerTrust 时,调用以下方法
- (instancetype)initWithTrust:(SecTrustRef)trust;
+ (NSURLCredential *)credentialForTrust:(SecTrustRef)trust;
2、NSURLSessionTaskDelegate
NSURLSession 实例调用其委托来处理 task 级事件的协议方法
2.1、处理 task 的生命周期:
URLSession:task:didCompleteWithError: 告诉委托已完成传输数据的任务。
服务器错误不会通过 Error。委托通过 Error 参数接收到的唯一错误是客户端错误,例如无法解析主机名或连接到主机。
2.2、处理 task 的重定向:
URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler: 告诉委托,远程服务器请求HTTP重定向
此方法仅用于 defaultSessionConfiguration 和 ephemeralSessionConfiguration 的会话。后台会话中的任务自动重定向。
说一下它的几个参数:
- response 服务器对原始请求的响应对象
- request 用新位置填充的 NSURLRequest
- completionHandler 可向其传入重定向的新的NSURLRequest对象,那么会正常执行重定向请求,也可以传入 nil,则不执行重定向请求并以当前响应体作为重定向后的响应
2.3、处理上传 task
2.3.1 URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend: 当执行 upload task 时,系统会定期的调用此代理方法,报告上传请求体的进度
讨论下它的几个参数:
- bytesSent 从上次调用委托方法以来发送的字节数
- totalBytesSent 到目前为止发送的字节总数
- totalBytesExpectedToSend body data 的预期长度。URL加载系统可以通过三种方式确定上传数据的长度:
<1>从作为上传 body 提供的 NSData 对象的长度开始;
<2>从磁盘上作为上传任务(而不是下载任务)的上传 body 提供的文件长度。
<3>如果设置请求对象,则从 Content-Length 开始
2.3.2 URLSession:task:needNewBodyStream:
当调用以下方法创建的 task 发起请求时,必须实现该代理方法,以提供 request 需要的 stream
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;
因为从输入流读数据是不可逆的,所以在上传失败时,可能会重新调用该方法读取流。在方法中执行 completionHandler 参数,向其传入 NSInputStream 对象。
这个任务在两种情况下调用:
- 如果任务是使用 uploadTaskWithStreamedRequest: 创建的,则提供原来的request body stream
- 如果任务需要重新发送由于身份验证挑战或其他可恢复的服务器错误而具有体流的请求,则提供替换的请求体流。
2.4、处理身份认证
URLSession:task:didReceiveChallenge:completionHandler:
此方法处理 task 级身份验证。NSURLSessionDelegate 协议还提供了会话级别的身份验证委托方法。所调用的方法取决于身份验证挑战的类型:
- 对于 session 级别的验证: 当 authenticationMethod 的值为:
NSURLAuthenticationMethodNTLM、
NSURLAuthenticationMethodNegotiate、
NSURLAuthenticationMethodClientCertificate和
NSURLAuthenticationMethodServerTrust时,
系统会先尝试调用 session 级的处理方法,若 session 级未实现,则尝试调用 task 级的处理方法; - 对于非 session 级别的验证:直接调用 task 级的处理方法,无论 session 级方法是否实现。
关于它的参数,在< 1.2 处理 session 身份验证 > 已经介绍过,这里不再过多阐述。
2.5、处理延迟和等待任务
2.5.1、URLSession:task:willBeginDelayedRequest:completionHandler:
延迟的 URL session task 现在将开始加载,当具有延迟启动时间的后台会话任务(如使用earliestBeginDate属性设置的)准备启动时,将调用此方法。只有在等待网络加载并需要被新请求替换时,该委托方法才应该被实现。
要继续加载,委托必须调用完成处理程序,传递一个属性,指示任务应该如何进行。传递NSURLSessionDelayedRequestCancel处理等同于直接调用任务上的cancel。
关于它的参数 completionHandler :如何处理延迟任务
- disposition 告诉 task 如何进行的配置
- newRequest 在 disposition 为NSURLSessionDelayedRequestUseNewRequest 时才使用的新请求对象
其中,disposition 是个 NSURLSessionDelayedRequestDisposition 类型的枚举
值 | 描述 |
---|---|
NSURLSessionDelayedRequestCancel | 取消该任务 |
NSURLSessionDelayedRequestContinueLoading | 继续执行原始请求 |
NSURLSessionDelayedRequestCancel | 使用新请求执行下载 |
2.5.2、URLSession:taskIsWaitingForConnectivity:
告诉委托,任务正在等待,直到合适的连接可用后才开始网络加载。
如果 NSURLSessionConfiguration 的 waitsForConnectivity 属性为YES,并且无法获得足够的连接性,则调用此方法。可以使用这个方法更新用户界面:例如,通过显示脱机模式或只显示蜂窝模式。
每个任务最多调用此方法一次,并且只在连接最初不可用时调用。它从来不需要后台会话,因为这些会话忽略了waitsForConnectivity。
2.6、收集任务指标
URLSession:task:didFinishCollectingMetrics:
告诉委托会话已完成为任务收集度量,可以用来流量监控分析。
3、NSURLSessionDataDelegate
session 调用NSURLSessionDataDelegate来处理NSURLSessionDataTask,该协议主要用来处理dataTask的数据处理(比如接收到响应,接收到数据,是否缓存数据)
3.1、处理 task 生命周期
3.1.1、URLSession:dataTask:didReceiveResponse:completionHandler:
当 DataTask 收到响应时,会调用此代理方法;
在方法中要执行 block 参数 completionHandler,向其传入 NSURLSessionResponseDisposition 枚举类型参数,指明继续正常返回响应体,还是取消请求,或者将 task 转变为 download task。
传入 NSURLSessionResponseBecomeDownload,将 task 转换为 download task 后,会调用代理方法 URLSession:dataTask:didBecomeDownloadTask:
传入 NSURLSessionResponseBecomeStream,将 task 转换为 StreamTask 后,会调用代理方法 URLSession:dataTask:didBecomeStreamTask:
如果你的request中包含的content-type支持 multipart/x-mixed-replace 时,服务器会将数据分片传回来,而且每次传回来的数据会覆盖之前的数据。每次返回新的数据时,session都会调用该函数,你应该在这个函数中合理地处理先前的数据,否则会被新数据覆盖。如果你没有提供该方法的实现,那么session将会继续任务,也就是说会覆盖之前的数据。
关于 disposition :NSURLSessionResponseDisposition upload session 在收到初始头后应该如何进行
值 | 描述 |
---|---|
NSURLSessionResponseCancel | 该task会被取消 |
NSURLSessionResponseAllow | 该task正常进行 |
NSURLSessionResponseBecomeDownload | 转成一个downloadTask |
NSURLSessionResponseBecomeStream | 转成一个StreamTask |
3.1.2、URLSession:dataTask:didBecomeDownloadTask:
当 3.1.1 URLSession:dataTask:didReceiveResponse:completionHandler: 中的completionHandler 传入 NSURLSessionResponseBecomeDownload 来转换请求以使用下载时,会话调用这个方法来为您提供新的下载任务。在此调用之后,会话委托不再接收与原始数据任务相关的其他委托方法调用。
3.1.3、URLSession:dataTask:didBecomeStreamTask:
当 3.1.1 URLSession:dataTask:didReceiveResponse:completionHandler: 中的completionHandler 传入 NSURLSessionResponseBecomeStream 来转换请求以使用流时,会话调用这个委托方法来为您提供新的流任务。在此调用之后,会话委托不再接收与原始数据任务相关的其他委托方法调用。
3.2、接收数据 URLSession:dataTask:didReceiveData:
当我们获取到数据就会调用,会被反复调用,请求到的数据就在这被拼装完整
3.3、处理缓存
URLSession:dataTask:willCacheResponse:completionHandler:
当task接收到所有期望的数据后,session会调用此代理方法。如果你没有实现该方法,那么就会使用创建session时使用的configuration对象决定缓存策略。这个代理方法最初的目的是为了阻止缓存特定的URLs或者修改NSCacheURLResponse对象相关的userInfo字典。
该方法只会当request决定缓存response时候调用。
作为准则,responses只会当以下条件都成立的时候返回缓存:
该request是HTTP或HTTPS URL的请求(或者你自定义的网络协议,并且确保该协议支持缓存)
确保request请求是成功的(返回的status code为200-299)
返回的response是来自服务器端的,而非缓存中本身就有的
提供的NSURLRequest对象的缓存策略要允许进行缓存
服务器返回的response中与缓存相关的header要允许缓存
该response的大小不能比提供的缓存空间大太多(比如你提供了一个磁盘缓存,那么response大小一定不能比磁盘缓存空间还要大5%)
4、NSURLSessionDownloadDelegate
NSURLSession 实例调用其委托来处理下载任务,是一个的 task 级事件的协议。
4.1、处理 Download 生命周期
URLSession:downloadTask:didFinishDownloadingToURL:
下载完成的时候调用
这个代理方法是 SURLSessionDownloadDelegate 协议中必须实现的,方法参数传入一个下载文件的临时保存路径,应用需要读取文件的内容或将文件移到其他地方,因为代理方法执行结束后,该路径下的文件会被删除,注意,如果读取文件内容的话,则要在单独的线程中进行,否则可能会造成页面卡死,影响用户体验。
4.2、恢复暂停下载
URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:
当下载被取消或者失败后重新恢复下载时调用
函数作用:告诉代理,下载任务重新开始下载了。
函数讨论:
如果一个正在下载任务被取消或者失败了,你可以请求一个resumeData对象(比如在userInfo字典中通过NSURLSessionDownloadTaskResumeData这个键来获取到resumeData)并使用它来提供足够的信息以重新开始下载任务。
随后,你可以使用resumeData作为downloadTaskWithResumeData:或downloadTaskWithResumeData:completionHandler:的参数。当你调用这些方法时,你将开始一个新的下载任务。一旦你继续下载任务,session会调用它的代理方法URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:其中的downloadTask参数表示的就是新的下载任务,这也意味着下载重新开始了。
总结一下:
其实这个就是用来做断点续传的代理方法。可以在下载失败的时候,拿到我们失败的拼接的部分resumeData,然后用去调用downloadTaskWithResumeData:就会调用到这个代理方法来了。
其中注意:fileOffset这个参数,如果文件缓存策略或者最后文件更新日期阻止重用已经存在的文件内容,那么该值为0。否则,该值表示当前已经下载data的偏移量。
方法中仅仅调用了downloadTaskDidResume自定义Block。
4.3、接收进度更新
URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:
周期性地通知下载进度调用
bytesWritten 表示自上次调用该方法后,接收到的数据字节数
totalBytesWritten 表示目前已经接收到的数据字节数
totalBytesExpectedToWrite 表示期望收到的文件总字节数,是由Content-Length header提供。
如果没有提供,默认是NSURLSessionTransferSizeUnknown。
5、NSURLSessionStreamDelegate
NSURLSession 实例调用其委托来处理流任务,是一个 task 级事件的协议
5.1、处理重编路由
URLSession:betterRouteDiscoveredForStreamTask:
告诉委托,已为流检测到更好的到主机的路由。
当URL加载系统确定到端点主机的更好路径可用时,将调用此方法。例如,当Wi-Fi接口可用时,可以调用此方法。
您应该考虑完成待完成的工作并创建一个新的流任务,以便在它们可用时利用更好的路由。
5.2、完成流捕获
URLSession:streamTask:didBecomeInputStream:outputStream:
只在完成所有的入队列读和写流任务之后才被调用
5.3、处理关闭事件
5.3.1、URLSession:readClosedForStreamTask:
即使没有读取,也可以调用此方法。此方法并不表示流到达文件的结束(EOF),这样就不能读取更多的数据。
5.3.2、URLSession:writeClosedForStreamTask:
即使没有写操作正在进行,也可以调用此方法。
......................... 未完,持续更新中 ........................
网友评论