AFNetworking3.1源码解读<四>

上篇文章AFNewworking3.1源码解读<三>主要学习了Response Serialization相关部分,着篇主要来学习Reachability和Security。

Reachability

监听网络

reachability主要是基于底层框架SCNetworkReachability实现的,在startMonitoring中设置了网络状态变化时的回调函数,以及最重要的把reachability添加到了MainRunLoop中的kCFRunLoopCommonModes,这样就实现了监听事件一直存活(添加入MainRunLoop,reability相当于一个source),并且当网络状态发生变化时及时响应(设置了事件回调)。相关代码如下:

//设置callback
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);

//添加入RunLoop   SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);

网络状态获取

  1. 直接获取reachable(有网)、reachableViaWWAN(移动网络)、reachableViaWiFi(Wi-Fi网络)属性。
  2. 监听AFNetworkingReachabilityDidChangeNotification通知,ReachabilityManager会在网路状态发生变化时主动发送通知。
  3. 设置网络状态发生变化时的block,setReachabilityStatusChangeBlock:在网络发生变化时也会回调。
  4. KVO reachable、reachableViaWWAN、reachableViaWiFi,当网络网络状态变化时Reability会主动更新这些属性值,所以如果KVO了这些属性就能实时获取到网络状态了。这三个属性的值都依赖网路状态属性,所以AFNetworkReachabilityManager实现了keyPathsForValuesAffectingValueForKey方法,相关代码如下:
1
+  (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
  if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {
      return [NSSet setWithObject:@"networkReachabilityStatus"];
  }
  return [super keyPathsForValuesAffectingValueForKey:key];
  }

停止监听网络

停止监听网络非常简单,只需把reability事件从MainRunloop中删除即可,相关代码如下:

- (void)stopMonitoring {
    if (!self.networkReachability) {
        return;
    }
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}

Security

AFNetworking支持3种Security policy,分别为AFSSLPinningModeNone、AFSSLPinningModePublicKey、AFSSLPinningModeCertificate。当Security policy为AFSSLPinningModeNone时只校验服务器发送的证书是否合法;当Security policy为AFSSLPinningModePublicKey时则通过校验本地certificate中public key和服务器发送的certificate中public key来验证合法性;当Security policy为AFSSLPinningModeCertificate时校验本地certificate和服务器发送的certificate来验证合法性。

certificate获取

AFNetworking会默认获取编译工程中.cer文件,而.cer就是certificate,pinnedCertificates属性中存放着这些.cer文件内容。相关代码如下:

+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
    NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];

        NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
        for (NSString *path in paths) {
            NSData *certificateData = [NSData dataWithContentsOfFile:path];
            [certificates addObject:certificateData];
        }

        return [NSSet setWithSet:certificates];
    }

public key 获取

certificate中存放有public key、证书主体、数字签名等,所以public key就是从上面certificate文件中取出,主要用到的API是Security framework。

校验入口

当收到NSURLSessionTaskDelegate的

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                            didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge 
                                completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler;

接口时就需要客户端校验服务器发送的证书了,具体校验规则就3种,校验内容也在开头说过了,具体校验代码都是基于Security framework,这儿就不粘贴代码了,详细请看接口:

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                  forDomain:(NSString *)domain