上一篇主要介绍了代理的转发思想,NSURLSession的代理在类AFURLSessionManager
中实现,转发到AFURLSessionManagerTaskDelegate
自定义的代理中。从这一篇开始会用几篇介绍这个NSURLSession的代理的用法。
先看代理方法
/* The last message a session receives. A session will only become
* invalid because of a systemic error or when it has been
* explicitly invalidated, in which case the error parameter will be nil.
*/
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error;
下面是代理方法的实现
@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;
typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error);
- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error
{
if (self.sessionDidBecomeInvalid) {
self.sessionDidBecomeInvalid(session, error);
}
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}
如果你使用finishTasksAndInvalidate
函数使该session失效,
那么session首先会先完成最后一个task,然后再调用URLSession:didBecomeInvalidWithError:
代理方法,
如果你调用invalidateAndCancel
方法来使session失效,那么该session会立即调用上面的代理方法。
这个就是该代理方法调用的时机。
这个代理方法做了两个事情:
block - sessionDidBecomeInvalid
我们先看一下系统定义的API
/* If implemented, when a connection level authentication challenge
* has occurred, this delegate will be given the opportunity to
* provide authentication credentials to the underlying
* connection. Some types of authentication will apply to more than
* one request on a given connection to a server (SSL Server Trust
* challenges). If this delegate message is not implemented, the
* behavior will be to use the default handling, which may involve user
* interaction.
*/
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;
下面看一下在该类中的实现。
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
// 此处服务器要求客户端的接收认证挑战方法是NSURLAuthenticationMethodServerTrust
// 也就是说服务器端需要客户端返回一个根据认证挑战的保护空间提供的信任(即challenge.protectionSpace.serverTrust)产生的挑战证书。
// 而这个证书就需要使用credentialForTrust:来创建一个NSURLCredential对象
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// 基于客户端的安全策略来决定是否信任该服务器,不信任的话,也就没必要响应挑战
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
// 创建挑战证书(注:挑战方式为UseCredential和PerformDefaultHandling都需要新建挑战证书)
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
//取消挑战
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
这里其实就是HTTPS认证,服务器向客户端进行质询和挑战。这个类似的其实在SDWebImage
中讲过,这里也简单的看一下。
web服务器接收到客户端请求时,有时候需要先验证客户端是否为正常用户,再决定是够返回真实数据。这种情况称之为服务端要求客户端接收挑战(NSURLAuthenticationChallenge *challenge)
。接收到挑战后,客户端要根据服务端传来的challenge来生成completionHandler所需的NSURLSessionAuthChallengeDisposition disposition
和NSURLCredential *credential
(disposition指定应对这个挑战的方法,而credential是客户端生成的挑战证书,注意只有challenge中认证方法为NSURLAuthenticationMethodServerTrust
的时候,才需要生成挑战证书)。最后调用completionHandler回应服务器端的挑战。
当某个session使用SSL/TLS
协议,第一次和服务器端建立连接的时候,服务器会发送给iOS客户端一个证书,此方法允许你的app验证服务期端的证书链(certificate keychain)
注:如果你没有实现该方法,该session会调用其NSURLSessionTaskDelegate
的代理方法URLSession:task:didReceiveChallenge:completionHandler:
。
先看一下系统的API。
/* If an application has received an
* -application:handleEventsForBackgroundURLSession:completionHandler:
* message, the session delegate will receive this message to indicate
* that all messages previously enqueued for this session have been
* delivered. At this time it is safe to invoke the previously stored
* completion handler, or to begin any internal updates that will
* result in invoking the completion handler.
*/
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session API_AVAILABLE(ios(7.0), watchos(2.0), tvos(9.0)) API_UNAVAILABLE(macos);
下面看一下AFN中的实现
@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession;
typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session);
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
if (self.didFinishEventsForBackgroundURLSession) {
dispatch_async(dispatch_get_main_queue(), ^{
self.didFinishEventsForBackgroundURLSession(session);
});
}
}
UIApplicationDelegate
会发送一个application:handleEventsForBackgroundURLSession:completionHandler:
消息。该消息包含了对应后台的session的identifier,而且这个消息会导致你的app启动。你的app随后应该先存储completion handler,然后再使用相同的identifier创建一个background configuration,并根据这个background configuration
创建一个新的session。这个新创建的session会自动与后台任务重新关联在一起。URLSessionDidFinishEventsForBackgroundURLSession:
消息,这就意味着之前这个session中已经入队的所有消息都转发出去了,这时候再调用先前存取的completion handler是安全的,或者因为内部更新而导致调用completion handler
也是安全的。本篇主要介绍了
NSURLSessionDelegate
的三个代理方法。介绍了方法的使用场景和注意事项。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。