网络

OC之NSURLSessionDelegate

2018-06-23  本文已影响116人  苏沫离

NSURLSession 的 task 是异步回调的,所有的回调都是在委托代理里面处理, 苹果为我们提供了 NSURLSessionDelegate 的一系列代理方法供我们选择使用,首先,我们先了解下 NSURLSession 的相关代理:

NSURLSessionDelegate继承关系.png

这些代理都是继承关系,在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 级身份验证请求;此方法在两种情况下调用:

也就是说:当在 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

通知系统如何处理验证,需要传入两个参数:

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:

- (instancetype)initWithUser:(NSString *)user password:(NSString *)password persistence:(NSURLCredentialPersistence)persistence;
+ (NSURLCredential *)credentialWithUser:(NSString *)user password:(NSString *)password persistence:(NSURLCredentialPersistence)persistence;
- (instancetype)initWithIdentity:(SecIdentityRef)identity certificates:(nullable NSArray *)certArray persistence:(NSURLCredentialPersistence)persistence;
+ (NSURLCredential *)credentialWithIdentity:(SecIdentityRef)identity certificates:(nullable NSArray *)certArray persistence:(NSURLCredentialPersistence)persistence;
- (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 的会话。后台会话中的任务自动重定向。
说一下它的几个参数:

2.3、处理上传 task
2.3.1 URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend: 当执行 upload task 时,系统会定期的调用此代理方法,报告上传请求体的进度

讨论下它的几个参数:

2.3.2 URLSession:task:needNewBodyStream:

当调用以下方法创建的 task 发起请求时,必须实现该代理方法,以提供 request 需要的 stream

- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;

因为从输入流读数据是不可逆的,所以在上传失败时,可能会重新调用该方法读取流。在方法中执行 completionHandler 参数,向其传入 NSInputStream 对象。
这个任务在两种情况下调用:

2.4、处理身份认证

URLSession:task:didReceiveChallenge:completionHandler:
此方法处理 task 级身份验证。NSURLSessionDelegate 协议还提供了会话级别的身份验证委托方法。所调用的方法取决于身份验证挑战的类型:

关于它的参数,在< 1.2 处理 session 身份验证 > 已经介绍过,这里不再过多阐述。

2.5、处理延迟和等待任务
2.5.1、URLSession:task:willBeginDelayedRequest:completionHandler:

延迟的 URL session task 现在将开始加载,当具有延迟启动时间的后台会话任务(如使用earliestBeginDate属性设置的)准备启动时,将调用此方法。只有在等待网络加载并需要被新请求替换时,该委托方法才应该被实现。
要继续加载,委托必须调用完成处理程序,传递一个属性,指示任务应该如何进行。传递NSURLSessionDelayedRequestCancel处理等同于直接调用任务上的cancel。

关于它的参数 completionHandler :如何处理延迟任务

其中,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:

即使没有写操作正在进行,也可以调用此方法。

......................... 未完,持续更新中 ........................

上一篇 下一篇

猜你喜欢

热点阅读