AFNetworking源码 - AFSecurityPolicy 和 AFNetworkReachabilityManager

AFNetworking 源码分析 一.AFNetworking的整体架构 1.AFN的整体架构大致分为网络通信模块,网络监听状态模块,网络通信安全策略模块,网络通信信息序列化&反序列化模块等构成 AFNetworking实际上只是对NSURSession的封装,提供一些API方便我们在iOS开发中发出网络请求 阅读详情

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:]

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

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

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

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

static BOOL AFServerTrustIsValid(SecTrustRef serverTrust)

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

static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust)

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

static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust)

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

2.2 + [AFSecurityPolicy policyWithPinningMode:withPinnedCertificates:]

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

  
  1. // 初始化AFSecurityPolicy对象的SSLPinningMode和pinnedCertificates两个属性
  2. + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
  3. AFSecurityPolicy *securityPolicy = [[self alloc] init];
  4. securityPolicy.SSLPinningMode = pinningMode;
  5. [securityPolicy setPinnedCertificates:pinnedCertificates];
  6. return securityPolicy;
  7. }
  8. // 此函数设置securityPolicy中的pinnedCertificates属性
  9. // 注意还将对应的self.pinnedPublicKeys属性也设置了,该属性表示的是对应证书的公钥(与pinnedCertificates中的证书是一一对应的)
  10. - (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
  11. _pinnedCertificates = pinnedCertificates;
  12. if (self.pinnedCertificates) {
  13. NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
  14. for (NSData *certificate in self.pinnedCertificates) {
  15. id publicKey = AFPublicKeyForCertificate(certificate);
  16. if (!publicKey) {
  17. continue;
  18. }
  19. [mutablePinnedPublicKeys addObject:publicKey];
  20. }
  21. self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
  22. } else {
  23. self.pinnedPublicKeys = nil;
  24. }
  25. }

static id AFPublicKeyForCertificate(NSData *certificate)

  
  1. // 此函数没什么特别要提及的,和AFPublicKeyTrustChainForServerTrust实现的原理基本一致
  2. // 区别仅仅在该函数是返回单个证书的公钥(所以传入的参数是一个证书),而AFPublicKeyTrustChainForServerTrust返回的是serverTrust的证书链中所有证书公钥
  3. static id AFPublicKeyForCertificate(NSData *certificate) {
  4. id allowedPublicKey = nil;
  5. SecCertificateRef allowedCertificate;
  6. SecCertificateRef allowedCertificates[1];
  7. CFArrayRef tempCertificates = nil;
  8. SecPolicyRef policy = nil;
  9. SecTrustRef allowedTrust = nil;
  10. SecTrustResultType result;
  11. // 因为此处传入的certificate参数是NSData类型的,所以需要使用SecCertificateCreateWithData来将NSData对象转化为SecCertificateRef对象
  12. allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
  13. __Require_Quiet(allowedCertificate != NULL, _out);
  14. allowedCertificates[0] = allowedCertificate;
  15. tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL);
  16. policy = SecPolicyCreateBasicX509();
  17. __Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out);
  18. __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
  19. allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
  20. _out:
  21. if (allowedTrust) {
  22. CFRelease(allowedTrust);
  23. }
  24. if (policy) {
  25. CFRelease(policy);
  26. }
  27. if (tempCertificates) {
  28. CFRelease(tempCertificates);
  29. }
  30. if (allowedCertificate) {
  31. CFRelease(allowedCertificate);
  32. }
  33. return allowedPublicKey;
  34. }

3. AFNetworkReachabilityManager


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

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

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

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

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

static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block)

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

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

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

image

4. 总结


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


转:http://www.cnblogs.com/polobymulberry/p/5174298.html

AI驱动全栈开发实战:基于Spec与Codex的规格驱动开发指南 1. 先搞清楚“AI重构前端全栈”到底在解决什么实际问题 最近很多人在聊“AI重构前端全栈”,听起来像是又一个营销概念。但如果你真的拆开看,它核心解决的是一个非常具体且痛苦的问题: 一个开发者,如何从零开始,系统性地、不跑偏地完成一个完整项目,并且保证代码质量和架构一致性。 传统开发流程里,产品经理出PRD,前后端开会对齐,前端画页面,后端写接口,测试再介入。这个过程沟通成本高,文档容易过时,新人上手慢。而“AI重构”的思路,是试图用一套 规范化的指令(Spec) 来驱动AI(比如Codex这类模型)生成代 阅读详情

相关推荐

Cesium体渲染实战:从医学CT到气象数据的3D可视化全攻略

本文详细介绍了如何利用Cesium体渲染技术实现医学CT和气象数据的3D可视化。从数据预处理到交互式渲染,涵盖了DICOM和NetCDF格式的处理技巧,以及性能优化的实战策略,帮助开发者高效处理复杂的三维数据集。

weixin_28717431的博客 420

Qt Creator设计器里也能拖拽?5步将你的QML自定义控件变成可视化插件(Qt6.5+)

本文详细介绍了如何在Qt Creator设计器中实现QML自定义控件的可视化拖拽功能(Qt6.5+)。通过5个步骤,从构建可插件化的QML控件基础到部署插件到设计器环境,帮助开发者将自定义控件转化为设计器插件,提升团队协作效率。特别适合需要频繁调整UI样式的开发场景。

weixin_29214559的博客 170

iOS使用SecKeyEncrypt加密的问题

项目需要RSA加密,网上看了下,按照以下代码从证书中获取公钥加密:  NSString *certPath = [[NSBundle mainBundle] pathForResource:@"myidentifier" ofType:@"cer"];     SecCertificateRef myCertificate = nil;     NSData *certificateData 

474

<Verilog实现除法器> Verilog实现牛顿-拉夫逊(Newton-Raphson)除法器

阿斯顿阿斯顿啊啊是打发士大夫

IC跳跳鱼的博客 3585

WGCNA分析中那些没人告诉你的坑:以肺结核差异基因模块筛选为例

本文深入探讨WGCNA分析在肺结核差异基因模块筛选中的七个关键陷阱,包括soft-thresholding选取、样本离群值检测、模块合并阈值调整等。通过实际案例和R代码示例,揭示生信数据挖掘中常被忽视的技术细节,帮助研究者避免常见错误,提升分析结果的可靠性和生物学意义。

weixin_29218509的博客 261

验证 HTTPS 请求的证书(五)

自 iOS9 发布之后,由于新特性 App Transport Security 的引入,在默认行为下是不能发送 HTTP 请求的。很多网站都在转用 HTTPS,而 AFNetworking 中的 AFSecurityPolicy 就是为了阻止中间人攻击,以及其它漏洞的工具。 AFSecurityPolicy 主要作用就是验证 HTTPS 请求的证书是否有效,如果 app 中有一些敏感信

wsh7365062的博客 2451

嘉立创EDA报错:元件引脚与焊盘未对应报错解决记录

原件引脚与焊盘未对应报错解决方法,一文总结!

2303_77013775的博客 1万+

rsa从cer证书中拿公钥进行加密

起初,读召辉给的cer证书的时候, SecCertificateCreateWithData 总是返回为nil,原因是因为这个证书是服务器自己生成的,不是正规的CA机构颁发的,苹果安全性要求是很高的,所以这个读不出来,直接导致后面的崩溃。后来折腾了半天,比如直接从证书里面导出字符串作为公钥,也不行,安卓这样也是不行的。             后来迪哥又生成了个der证书,这样读就是正常

ios_xumin的专栏 6485

华为eNSP Pro实战:NE/CE设备SSH远程配置避坑指南

本文针对华为eNSP Pro用户在模拟多台NE路由器或CE交换机时遇到的浏览器卡顿问题,提供了详细的SSH远程配置避坑指南。通过对比浏览器与SSH客户端的资源占用和操作体验,文章详细阐述了为NE和CE设备配置SSH服务的关键步骤、命令差异及常见错误排查方法,旨在帮助用户提升实验效率,实现稳定高效的远程管理。

weixin_29196891的博客 219

【乐鑫】唤醒词修改方法

乐鑫科技的语音识别模块(如ESP-SR)支持自定义唤醒词,但需通过官方工具链完成。

以实战经验介绍行业知识 823

AFNetworking 3.0 源码阅读笔记(七)

原文:http://itangqi.me/2016/05/20/the-notes-of-learning-afnetworking-seven/ 前言 自 iOS 9 发布之后,由于新特性 App Transport Security 的引入,在默认行为下是不能发送 HTTP 请求的。很多网站都在转用 HTTPS,而 AFNetworking 中的 AFSecurityPolicy

冷小G的技术博客 969

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

AFN源码分析总结

文章目录AFN源码分析总结:序列换和反序列化:multipart/form-data:POST编码类型: enctypeURL编码: 百分编码: AFPercentEscapedStringFromString:User-Agent:NSError和NSURLErrorNSJSONReadingOptions和NSJSONWritingOptionsNSURLSession:NSURLSessio...

高级软件开发工程师--周玉的博客 1744

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

AFNetworking 2.0

大名鼎鼎的开源网络库AFNetworking 2.0,目前只是翻译了Github上的链接文章,使用教程请点击 http://www.cnblogs.com/YouXianMing/p/3651462.html   https://github.com/AFNetworking/AFNetworking   AFNetworking is a delightful ne

sinat_15885675的专栏 251

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

AFNetworking源码之AFSecurityPolicy模块

续AFNetworking综述,NSURLConnection模块,

seven的技术交流博客 2278

AFNetworking源码学习

AFNetworking源码学习 简介 AFNetWorking是使用Objective-c开发iOS程序主流的网络请求开源库。 AFNetworking组织结构 AFNetWorking主要分为5个部分。NSURLSession、Reachability、security、Serialization、UIKit。其中NSURLSession是整个网络请求开源库的灵魂,对外提供了丰富的网络请求接口...

无神的博客 749

[iOS开发]AFNetworking源码学习

简介 浅浅解读一下AFNetworking

weixin_52192405的博客 1468

iOS_AFNetworking 结构解析+用例分析+源码阅读

AFNetworking Github OC语言排第一, 凝聚了众多 封装了NSURLSession的网络请求, 线程NSOperation最大并发数是1, 保证每个网络请求串行进行 Session中的taskData都是异步执行 -由NSURLSession,Security,Reachability,Serialization,UIKit五部分组成 对网络状态的监听和处理 网络通信信息序列化/反序列化 判断method:Get、Head、Delete / Post、Put 区分不同的参数拼接方式 对参.

mikimo的博客 964

AFNetworking 3.0 源码解读

一个非常棒的AFNetworking 3.0 源码解读系列文章及自己整理的完整版下载

Minecraft 1108

AFNetworking源码浅析

本文将从最简单的GET请求方法的使用入手,由表及里,逐步探究AFNetworking如何封装处理原生的网络请求。 一、AFNetworking的简单使用 -(void)getDemo{ AFHTTPSessionManager *manager =[AFHTTPSessionManager manager]; NSDictionary *dict = ...

dfgwang1202的博客 203

AFNetworking 源码阅读

AFNetworking 3.1.0 AFNetworking 主要的几个类或协议: AFURLSessionManager AFHTTPSessionManager AFNetworkReachabilityManager AFSecurityPolicy &lt;AFURLRequestSerialization&gt; &lt;AFURLResponseSerialization&gt...

weixin_34194379的博客 143

AFNetworking源码简单分析

本文将从最简单的GET请求方法的使用入手,由表及里,逐步探究AFNetworking如何封装处理原生的网络请求。 一、AFNetworking的简单使用 -(void)getDemo{ AFHTTPSessionManager *manager =[AFHTTPSessionManager manager]; NSDictionary *dict = @{...

qq_38455522的博客 268

AFNetworking源码简析

AFNetworking基本是苹果开发中网络请求库的标配,它是一个轻量级的网络库,专门针对iOS和OS X的网络应用设计,具有模块化的架构和丰富的APIs接口,功能强大并且使用简单,深受苹果应用开发人员的喜爱。

KelvinFlying的专栏 547

AFNetWorking(3.0)源码分析(一)——基本框架

框架组成         AFNetWorking是著名的网络通信框架,今天,我们就一起分析一下该框架的组成。         将AF下载导入工程后,下面是其包结构:         除去Support Files,可以看到AF分为如下5个功能模块: 网络通信模块(NSURLSession) 网络状态监听模块(Reachability) 网络通信安全策略模块(Security) 网络通

无忘无往 1万+

AFNetworking(v3.1.0) 源码解析

https://juejin.im/entry/5bf626b2f265da614e2bb7dd 一、架构 AFNetworking 一共分为 5 个模块,2 个核心模块和 3 个辅助模块: Core NSURLSession(网络通信模块) AFURLSessionManager(封装 NSURLSession) AFHTTPSessionManager(继承自 AFURLSe...

ZZQzzq123456的博客 295
上一篇: AFNetworking源码 - Multipart协议,AFURLRequestSerialization和AFURLResponseSerialization
下一篇: 多线程 - NSOperationQueue
直隶码农
博客等级 码龄19年 95粉丝 103原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值