• 【原】AFNetworking源码阅读(六)


    【原】AFNetworking源码阅读(六)

    本文转载请注明出处 —— polobymulberry-博客园

    1. 前言


    这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AFNetworking的网络安全策略,尤其指HTTPS(大家可以先简单了解下HTTPS)。再一个就是分析下AFNetworkReachabilityManager文件,看看AFNetworking如何解决网络状态的检测。

    2. AFSecurityPolicy - 网络安全策略


    之前我们在AFURLSessionManager中实现了NSURLSessionDelegate的代理方法- (void)URLSession:didReceiveChallenge:completionHandler:,其中我们提到过,如果iOS客户端需要向服务器发送一个凭证(Credential)来确认认证挑战(authentication challenge),那么先得使用AFNetworking的安全策略类-AFSecurityPolicy来检查服务器端是否可以信任(在authenticationMethod属性为NSURLAuthenticationMethodServerTrust情况下),而检查的方式就是通过- [AFSecurityPolicy evaluateServerTrust:forDomain:]这个函数。

    2.1 - [AFSecurityPolicy evaluateServerTrust:forDomain:]

    // 根据severTrust和domain来检查服务器端发来的证书是否可信
    // 其中SecTrustRef是一个CoreFoundation类型,用于对服务器端传来的X.509证书评估的
    // 而我们都知道,数字证书的签发机构CA,在接收到申请者的资料后进行核对并确定信息的真实有效,然后就会制作一份符合X.509标准的文件。证书中的证书内容包含的持有者信息和公钥等都是由申请者提供的,而数字签名则是CA机构对证书内容进行hash加密后得到的,而这个数字签名就是我们验证证书是否是有可信CA签发的数据。
    - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                      forDomain:(nullable NSString *)domain;

    具体函数我们直接看函数源码:

    - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                      forDomain:(NSString *)domain
    {
        /**
    self.allowInvalidCertificates==YES表示如果此处允许使用自建证书(服务器自己弄的CA证书,非官方),并且还想验证domain是否有效(self.validatesDomainName == YES),也就是说你想验证自建证书的domain是否有效。那么你必须使用pinnedCertificates(就是在客户端保存服务器端颁发的证书拷贝)才可以。但是你的SSLPinningMode为AFSSLPinningModeNone,表示你不使用SSL pinning,只跟浏览器一样在系统的信任机构列表里验证服务端返回的证书。所以当然你的客户端上没有你导入的pinnedCertificates,同样表示你无法验证该自建证书。所以都返回NO。最终结论就是要使用服务器端自建证书,那么就得将对应的证书拷贝到iOS客户端,并使用AFSSLPinningMode或AFSSLPinningModePublicKey
        if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
            // https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
            NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
            return NO;
        }
        // 此处设置验证证书的策略
        NSMutableArray *policies = [NSMutableArray array];
        
        if (self.validatesDomainName) {
            // 如果需要验证domain,那么就使用SecPolicyCreateSSL函数创建验证策略,其中第一个参数为true表示验证整个SSL证书链,第二个参数传入domain,用于判断整个证书链上叶子节点表示的那个domain是否和此处传入domain一致
            [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
        } else {
            // 如果不需要验证domain,就使用默认的BasicX509验证策略
            [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
        }
        // 为serverTrust设置验证策略,即告诉客户端如何验证serverTrust
        SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
        // 如果SSLPinningMode为 AFSSLPinningModeNone,表示你不使用SSL pinning,但是我允许自建证书,那么返回YES,或者使用AFServerTrustIsValid函数看看serverTrust是否可信任,如果信任,也返回YES
        if (self.SSLPinningMode == AFSSLPinningModeNone) {
            return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
        } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
            // 既不允许自建证书,而且使用AFServerTrustIsValid函数又返回NO,那么该serverTrust就真的不能通过验证了
            return NO;
        }
    
        switch (self.SSLPinningMode) {
            // 理论上,上面那个部分已经解决了self.SSLPinningMode)为AFSSLPinningModeNone)等情况,所以此处再遇到,就直接返回NO
            case AFSSLPinningModeNone:
            default:
                return NO;
           // 这个模式表示用证书绑定(SSL Pinning)方式验证证书,需要客户端保存有服务端的证书拷贝
            // 注意客户端保存的证书存放在self.pinnedCertificates中
            case AFSSLPinningModeCertificate: {
                NSMutableArray *pinnedCertificates = [NSMutableArray array];
                for (NSData *certificateData in self.pinnedCertificates) {
                   // 这里使用SecCertificateCreateWithData函数对原先的pinnedCertificates做一些处理,保证返回的证书都是DER编码的X.509证书
                    [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
                }
                // 将pinnedCertificates设置成需要参与验证的Anchor Certificate(锚点证书,通过SecTrustSetAnchorCertificates设置了参与校验锚点证书之后,假如验证的数字证书是这个锚点证书的子节点,即验证的数字证书是由锚点证书对应CA或子CA签发的,或是该证书本身,则信任该证书),具体就是调用SecTrustEvaluate来验证。
                SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
    
                if (!AFServerTrustIsValid(serverTrust)) {
                    return NO;
                }
    
                // 服务器端的证书链,注意此处返回的证书链顺序是从叶节点到根节点
                NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
                // 从服务器端证书链的根节点往下遍历,看看是否有与客户端的绑定证书一致的,有的话,就说明服务器端是可信的。因为遍历顺序正好相反,所以使用reverseObjectEnumerator
                for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]){
                    if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
                        return YES;
                    }
              }
                
                return NO;
            }
            // AFSSLPinningModePublicKey模式同样是用证书绑定(SSL Pinning)方式验证,客户端要有服务端的证书拷贝,只是验证时只验证证书里的公钥,不验证证书的有效期等信息。只要公钥是正确的,就能保证通信不会被窃听,因为中间人没有私钥,无法解开通过公钥加密的数据。
            case AFSSLPinningModePublicKey: {
                NSUInteger trustedPublicKeyCount = 0;
                // 从serverTrust中取出服务器端传过来的所有可用的证书,并依次得到相应的公钥
                NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
                // 依次遍历这些公钥,如果和客户端绑定证书的公钥一致,那么就给trustedPublicKeyCount加一
                for (id trustChainPublicKey in publicKeys) {
                    for (id pinnedPublicKey in self.pinnedPublicKeys) {
                        if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
                            trustedPublicKeyCount += 1;
                        }
                    }
                }
              // trustedPublicKeyCount大于0说明服务器端中的某个证书和客户端绑定的证书公钥一致,认为服务器端是可信的
                return trustedPublicKeyCount > 0;
            }
        }
        
        return NO;
    }

    上述函数实现中调用了AFSecurityPolicy的私有方法(注意evaluateServerTrust:forDomain:方法是AFSecurityPolicy比较重要的公开方法),下面我来逐个分析相应的函数实现。

    static BOOL AFServerTrustIsValid(SecTrustRef serverTrust)

    static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
        BOOL isValid = NO;
        SecTrustResultType result;
        // 对照下面的Require_noErr_Quiet函数解释,此处errorCode指的就是SecTrustEvaluate(serverTrust, &result)函数的返回值。如果serverTrust评估出错,那么就直接执行return isValid,默认isValid为NO。
        __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
        // 如果SecTrustEvaluate函数评估没出错,那么就看result的结果
        // 只有当result为kSecTrustResultUnspecified(此标志表示serverTrust评估成功,此证书也被暗中信任了,但是用户并没有显示地决定信任该证书),或者当result为kSecTrustResultProceed(此标志表示评估成功,和上面不同的是该评估得到了用户认可),这两者取其一就可以认为对serverTrust评估成功
        // 在下面有对result类型(SecTrustResultType)的简单讲解
        isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
    
    _out:
        return isValid;
    }
    
    // Require_noErr_Quiet是一个宏定义函数,表示如果errorCode不为0(0表示没有错误),那么就使用C语言中的go语句,跳到对应exceptionLabel地方开始执行代码。
    #ifndef __Require_noErr_Quiet
        #define __Require_noErr_Quiet(errorCode, exceptionLabel)                      
          do                                                                          
          {                                                                           
              if ( __builtin_expect(0 != (errorCode), 0) )                            
              {                                                                       
                  goto exceptionLabel;                                                
              }                                                                       
          } while ( 0 )
    #endif
    
    // kSecTrustResultUnspecified和kSecTrustResultProceed都是SecTrustResultType类型
    /** 
      SecTrustResultType中枚举值的含义包括两个方面:一个是指评估是否成功,另一个是指该评估结果是不是由用户决定的。对于是不是由用户决定的这个问题,上面kSecTrustResultUnspecified和kSecTrustResultProceed就是一个很好的例子
     */

    static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust)

    // 获取到serverTrust中证书链上的所有证书
    static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
        // 使用SecTrustGetCertificateCount函数t获取到serverTrust中需要评估的证书链中的证书数目,并保存到certificateCount中
        CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
        NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
        // 使用SecTrustGetCertificateAtIndex函数获取到证书链中的每个证书,并添加到trustChain中,最后返回trustChain
        for (CFIndex i = 0; i < certificateCount; i++) {
            SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
            [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
        }
    
        return [NSArray arrayWithArray:trustChain];
    }

    static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust)

    // 取出serverTrust中证书链上每个证书的公钥,并返回对应的该组公钥
    static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
        // 接下来的一小段代码和上面AFCertificateTrustChainForServerTrust函数的作用基本一致,都是为了获取到serverTrust中证书链上的所有证书,并依次遍历,取出公钥。
        SecPolicyRef policy = SecPolicyCreateBasicX509();
        CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
        NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
        for (CFIndex i = 0; i < certificateCount; i++) {
            SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
    
            SecCertificateRef someCertificates[] = {certificate};
            CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL);
    
            SecTrustRef trust;
            // 根据给定的certificates和policy来生成一个trust对象
            __Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);
    
            SecTrustResultType result;
            // 使用SecTrustEvaluate来评估上面构建的trust
            __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out);
            // 如果该trust符合X.509证书格式,那么先使用SecTrustCopyPublicKey获取到trust的公钥,再将此公钥添加到trustChain中
            [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];
    
        _out:
            // 注意释放资源
            if (trust) {
                CFRelease(trust);
            }
    
            if (certificates) {
                CFRelease(certificates);
            }
    
            continue;
        }
        CFRelease(policy);
        // 返回对应的一组公钥
        return [NSArray arrayWithArray:trustChain];
    }

    2.2 + [AFSecurityPolicy policyWithPinningMode:withPinnedCertificates:]

    AFSecurityPolicy中还有一些关于初始化的函数,比较重要的就数+ [AFSecurityPolicy policyWithPinningMode:withPinnedCertificates:]这个函数了。

    // 初始化AFSecurityPolicy对象的SSLPinningMode和pinnedCertificates两个属性
    + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
        AFSecurityPolicy *securityPolicy = [[self alloc] init];
        securityPolicy.SSLPinningMode = pinningMode;
    
        [securityPolicy setPinnedCertificates:pinnedCertificates];
    
        return securityPolicy;
    }
    
    // 此函数设置securityPolicy中的pinnedCertificates属性
    // 注意还将对应的self.pinnedPublicKeys属性也设置了,该属性表示的是对应证书的公钥(与pinnedCertificates中的证书是一一对应的)
    - (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
        _pinnedCertificates = pinnedCertificates;
    
        if (self.pinnedCertificates) {
            NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
            for (NSData *certificate in self.pinnedCertificates) {
                id publicKey = AFPublicKeyForCertificate(certificate);
                if (!publicKey) {
                    continue;
                }
                [mutablePinnedPublicKeys addObject:publicKey];
            }
            self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
        } else {
            self.pinnedPublicKeys = nil;
        }
    }

    static id AFPublicKeyForCertificate(NSData *certificate)

    // 此函数没什么特别要提及的,和AFPublicKeyTrustChainForServerTrust实现的原理基本一致
    // 区别仅仅在该函数是返回单个证书的公钥(所以传入的参数是一个证书),而AFPublicKeyTrustChainForServerTrust返回的是serverTrust的证书链中所有证书公钥
    static id AFPublicKeyForCertificate(NSData *certificate) {
        id allowedPublicKey = nil;
        SecCertificateRef allowedCertificate;
        SecCertificateRef allowedCertificates[1];
        CFArrayRef tempCertificates = nil;
        SecPolicyRef policy = nil;
        SecTrustRef allowedTrust = nil;
        SecTrustResultType result;
        // 因为此处传入的certificate参数是NSData类型的,所以需要使用SecCertificateCreateWithData来将NSData对象转化为SecCertificateRef对象
        allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
        __Require_Quiet(allowedCertificate != NULL, _out);
    
        allowedCertificates[0] = allowedCertificate;
        tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL);
    
        policy = SecPolicyCreateBasicX509();
        __Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out);
        __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
    
        allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
    
    _out:
        if (allowedTrust) {
            CFRelease(allowedTrust);
        }
    
        if (policy) {
            CFRelease(policy);
        }
    
        if (tempCertificates) {
            CFRelease(tempCertificates);
        }
    
        if (allowedCertificate) {
            CFRelease(allowedCertificate);
        }
    
        return allowedPublicKey;
    }

    3. AFNetworkReachabilityManager


    AFNetworkingReachabilityManager是我使用AFNetworking的时候,第一个接触到的类。当时主要是用这个类做一些网络状态判断。比如我当时做一个视频类的app时,考虑到网络如果是2G/3G/4G,那么最好是有一个switchButton来让用户选择是否使用2G/3G/4G来观看在线视频,此时你就需要判断当前网络状态,如果用户不允许2G/3G/4G网络观看在线视频,而此时手机网络又是2G/3G/4G网络,就得提示用户当前网络不支持播放在线视频。

    该类的使用方法很简单,我们看一个简单例子:

    // 设置networkReachabilityStatusBlock,根据不同网络状态,用户自定义处理方式
    AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
    [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        NSLog(@"network status '%@'", AFStringFromNetworkReachabilityStatus(status));
    }];
    // 启动网络监听
    [manager startMonitoring];

    其实networkReachabilityStatusBlock应该都好理解。关键是这个startMonitoring方法。大家是不是觉得很神奇,感觉就像app在系统后台有一个循环,不停地检测网络状态。那么具体是怎么做到的呢,这里先透露点:代码的核心使用了iOS提供的一系列SCNetworkReachability文件中的方法。

    - (void)startMonitoring {
        // 先停止之前的网络监听
        [self stopMonitoring];
        // networkReachability表示的是需要检测的网络地址的句柄
        if (!self.networkReachability) {
            return;
        }
    
        __weak __typeof(self)weakSelf = self;
        // 根据网络状态status来设置网络状态监听的回调函数callback
        AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
            __strong __typeof(weakSelf)strongSelf = weakSelf;
    
            strongSelf.networkReachabilityStatus = status;
            if (strongSelf.networkReachabilityStatusBlock) {
                strongSelf.networkReachabilityStatusBlock(status);
            }
    
        };
        /** context是一个结构体
             typedef struct {
             // 创建一个SCNetworkReachabilityContext结构体时,需要调用SCDynamicStore的创建函数,而此创建函数会根据version来创建出不同的结构体,SCNetworkReachabilityContext对应的version是0
             CFIndex        version;  
            // 下面两个block(release和retain)的参数就是info,此处表示的是网络状态处理的回调函数
              void *        __nullable info;
            // 该retain block用于对info进行retain,下面那个AFNetworkReachabilityRetainCallback核心就是调用了Block_copy(用于retain一个block函数,即在堆空间新建或直接引用一个block拷贝)
              const void    * __nonnull (* __nullable retain)(const void *info);
            // 该release block用于对info进行release,下面那个AFNetworkReachabilityReleaseCallback核心就是调用了Block_release(用于release一个block函数,即将block从堆空间移除或移除相应引用)
              void        (* __nullable release)(const void *info);
            // 提供info的description,此处调用为NULL
            CFStringRef    __nonnull (* __nullable copyDescription)(const void *info);
            } SCNetworkReachabilityContext;
         */
        SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
        /**
         // 给客户端指定对应target(该参数和需要检测网络状况的地址有一定关联,此处使用的是self.networkReachability),然后当这个target的网络状态变化时,告之SCNetworkReachabilityCallBack对象callout处理(此处使用的是AFNetworkReachabilityCallback),另外callout中使用到的参数包括target和context提供的info。
         Boolean
         SCNetworkReachabilitySetCallback    (
        SCNetworkReachabilityRef                                        target,
        SCNetworkReachabilityCallBack    __nullable    callout,
        SCNetworkReachabilityContext    * __nullable    context
    )                __OSX_AVAILABLE_STARTING(__MAC_10_3,__IPHONE_2_0);
         */
        SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
        /**
         此处表示在main RunLoop中以kCFRunLoopCommonModes形式处理self.networkingReachability
         */
        SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
        // 在后台检测self.networkingReachability的网络状态,并使用SCNetworkReachabilityGetFlags函数返回产生的flag,注意此处flag表示的就是网络的状态,后面会详细介绍每种flag对应的状态是什么
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
            SCNetworkReachabilityFlags flags;
            if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
                // AFPostReachabilityStatusChange函数就是先将flags转化为对应的AFNetworkReachabilityStatus变量,然后给我们的callback处理,后面会详解此函数
                AFPostReachabilityStatusChange(flags, callback);
            }
        });
    }

    static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block)

    static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
        // 使用AFNetworkReachabilityStatusForFlags函数将flags转化为status,提供给下面block使用
        AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
        dispatch_async(dispatch_get_main_queue(), ^{
            if (block) {
                block(status);
            }
            // 对于用户,可以使用KVO来观察status的变化,随后用户可以根据传过来的userInfo[AFNetworkingReachabilityNotificationStatusItem]获取到相应的status
            NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
            NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
            [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
        });
    }
    
    static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
        // 该网络地址可达
        BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
        // 该网络地址虽然可达,但是需要先建立一个connection
        BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); 
        // 该网络虽然也需要先建立一个connection,但是它是可以自动去connect的
        BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
        // 不需要用户交互,就可以connect上(用户交互一般指的是提供网络的账户和密码)
        BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
        // 如果isReachable==YES,那么就需要判断是不是得先建立一个connection,如果需要,那就认为不可达,或者虽然需要先建立一个connection,但是不需要用户交互,那么认为也是可达的
        BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));
        
        //  AFNetworkReachabilityStatus就四种状态Unknown、NotReachable、ReachableViaWWAN、ReachableViaWiFi,这四种状态字面意思很好理解,这里就不赘述了
        AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown;
        if (isNetworkReachable == NO) {
            status = AFNetworkReachabilityStatusNotReachable;
        }
    #if    TARGET_OS_IPHONE
        else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
            status = AFNetworkReachabilityStatusReachableViaWWAN;
        }
    #endif
        else {
            status = AFNetworkReachabilityStatusReachableViaWiFi;
        }
    
        return status;
    }

    此时AFNetworking如何判断网络状态的思路基本也理清楚了:先使用SCNetworkReachability相关函数得到网络状态,不过此时的网络状态还需要放到AFNetworking中封装一层,以提供适合用户使用的API(如isReachable、isReachableViaWWAN、isReachableViaWiFi),对于不同的网络状态,用户只需要定义自己的block进行处理就行

    最后,不知道大家对_networkReachability这个属性值是否有疑惑:源码中定义了一个sharedManager,那么sharedManager中的_networkReachability是如何设置的呢?一图以蔽之,我就不赘述了。

    image

    4. 总结


    这一篇作为AFNetworking源码阅读系列的最后一篇文章到此结束了。虽然还有部分UIKit+AFNetworking的内容没说,但是基本也没什么难度了,图片部分可以参考我之前写的SDWebImage源码分析。而其他UIKit部分,参考AFNetworking源码阅读第一篇文章中对AFNetworkActivityIndicatorManager和UIRefreshControl+AFNetworking的解读,因为基本思路都是一样的。

    5. 参考文章


  • 相关阅读:
    换种方式思考问题
    win7 下的open live writer代码插件
    vue-todolist
    vue-新建项目出错情况分析
    vue新建项目
    AJAX(一、基本知识)
    2016年度工作总结
    常看常遇见之一——BS架构VS CS架构
    thinkjs——session
    关于Listener
  • 原文地址:https://www.cnblogs.com/polobymulberry/p/5174298.html
Copyright © 2020-2023  润新知