iOS源码补完计划--AFNetworking 3.1.0源码研读

点击上方“iOS开发”,选择“置顶公众号”

关键时刻,第一时间送达!

640?640?wx_fmt=gif


目录


  • 准备工作

  • 功能模块

  • AFURLSessionManager/AFHTTPSessionManager

    • 在监听属性的时候、可以用NSStringFromSelector(@selector(xxx))这种方式来自动提示。

    • 功能AIP分层

    • 如何防止block循环引用

    • 把NSURLSession众多代理转化成了block

    • 消除编译器clang警告

    • 正则的简便写法

    • 如何做到对外只读、对内读写

    • 核心代码

    • 一些比较有意思的东西

  • AFNetworkReachabilityManager

    • 关于FOUNDATION_EXPORT和UIKIT_EXTERN的选择

    • .#if - #esle - #endif

    • 注册键值依赖

    • 四种网络状态

    • 开始暂停

    • 状态改变的回调block

    • 核心代码

    • 知识点

  • AFSecurityPolicy

    • __Require_Quiet判断

    • .cer文件在iOS里如何使用的

    • 三种验证模式

    • 核心代码

    • 知识点

  • AFHTTPRequestSerializer

  • AFHTTPResponseSerializer

    • 协议的应用

    • 如何在一个方法中返回两个NSError

    • NSIndexSet对象

    • 服务器返回的图片是压缩过的

    • AFURLResponseSerialization协议以及其解码方法

    • 核心代码

    • 知识点

  • 参考资料


准备工作


GitHu    b


使用版本3.1.0


PODS:
  - AFNetworking (3.1.0):
    - AFNetworking/NSURLSession (= 3.1.0)
    - AFNetworking/Reachability (= 3.1.0)
    - AFNetworking/Security (= 3.1.0)
    - AFNetworking/Serialization (= 3.1.0)
    - AFNetworking/UIKit (= 3.1.0)
  - AFNetworking/NSURLSession (3.1.0):
    - AFNetworking/Reachability
    - AFNetworking/Security
    - AFNetworking/Serialization
  - AFNetworking/Reachability (3.1.0)
  - AFNetworking/Security (3.1.0)
  - AFNetworking/Serialization (3.1.0)
  - AFNetworking/UIKit (3.1.0):
    - AFNetworking/NSURLSession DEPENDENCIES:
  - AFNetworking

SPEC CHECKSUMS:
  AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67

PODFILE CHECKSUM: 75e1e619317fd130ee494d35ddff3d9c614c4390

COCOAPODS: 1.3.1 


推荐在看AFN之前、先了解一下NSURLSession

不然感觉会看的一头雾水、也体会不到AFN的伟大之处


《iOS基础深入补完计划--网络模块NSURLSession概述》

https://www.jianshu.com/p/16ed20b0e7b8


功能模块


640?wx_fmt=jpeg


除了这四个服务性模块之外、UIKit文件夹下基本是对各种UI控件的扩展。


AFURLSessionManager/AFHTTPSessionManager


640?wx_fmt=jpeg


AFURLSessionManager流程


承接了主要的网络传输任务、实现了NSURLSession绝大部分的代理方法。


  • 核心代码


可以移步:《iOS源码补完计划--AFNetworking(一)》


一些比较有意思的东西


  • 在监听属性的时候、可以用NSStringFromSelector(@selector(xxx))这种方式来自动提示。


因为属性本身就是与其get方法同名、可以降低出错概率。


[self.uploadProgress addObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                             options:NSKeyValueObservingOptionNew context:NULL];


  • 功能AIP分层


AFURLSessionManager实现了所有的NSURLSessionDelegate。但同时又将其中某些需要处理复杂逻辑的代理传递给了AFURLSessionManagerTaskDelegate。使得代码更清晰、逻辑更明确。需要注意的是、AFURLSessionManagerTaskDelegate完全包裹在了AFURLSessionManager内部、外界完全感受到他的存在。但是又能做数据处理、这个架构设计真心很赞。


除此之外、AFURLSessionManager与AFHTTPSessionManager之间也做了很好的分层。


你可以单独使用AFURLSessionManager进行网络会话、也可以通过AFHTTPSessionManager更好的使用AFURLSessionManager进行HTTP请求。


  • 如何防止block循环引用


其实我几年前就听说AFN可以防止循环引用、但是一直没看。


今天找了找发现似乎已经没有了这段代码,所以个人推测现在不会引起循环引用的原因、应该是因为AFN都在作为单例使用、和self并不互相持有。


贴一段以前别人帖子里的代码:


//复写setCompletionBlock - (void)setCompletionBlock:(void (^)(void))block {
    [self.lock lock]; if (!block) {
        [super setCompletionBlock:nil];
    } else {
        __weak __typeof(self)weakSelf = self;
        [super setCompletionBlock:^ {
            __strong __typeof(weakSelf)strongSelf = weakSelf; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" //看有没有自定义的完成组,否则用AF的组 dispatch_group_t group = strongSelf.completionGroup ?: url_request_operation_completion_group(); //看有没有自定义的完成queue,否则用主队列 dispatch_queue_t queue = strongSelf.completionQueue ?: dispatch_get_main_queue(); #pragma clang diagnostic pop //调用设置的Block,在这个组和队列中 dispatch_group_async(group, queue, ^{
                block();
            }); //结束时候置nil,防止循环引用 dispatch_group_notify(group, url_request_operation_completion_queue(), ^{
                [strongSelf setCompletionBlock:nil];
            });
        }];
    }
    [self.lock unlock];
}


  • 把NSURLSession众多代理转化成了block


这个说实话我并不太暂停...


个人感觉block就是应该控制个数、而NSURLSession的代理加起来起码有二三十个。


如果到了这种数量级的数据传递、真的还是用代理吧、饶了我。


  • 消除编译器clang警告


其中Wgnu可以换成其他具体命令


#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" #pragma clang diagnostic pop 


  • 正则的简便写法


讲道理我还真第一次见


`A ?: B = A ? A : B` 


  • 如何做到对外只读、对内读写


.h中 @property (readonlynonatomicstrongnullableNSURL *baseURL;
.m中 @property (readwritenonatomicstrongNSURL *baseURL;


AFNetworkReachabilityManager


AFN中负责网络状态模块。在不同的网络状态下可以监听、或者实时查询、并且需要手动开启或者关闭。


  • 四种网络状态


未知、无网络、运营商网络、WiFi网络


  • 开始暂停

  • 状态改变的回调block


代码不多、详情可参阅《iOS源码补完计划--AFNetworking(二)》

https://www.jianshu.com/p/3506b9babdac


知识点


  • 关于FOUNDATION_EXPORT和UIKIT_EXTERN的选择


都可以代替宏来定义常量


FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;


有人说是如果文件基于FOUNDATION则用前者、反之则用后者。


二者都能替代#define、并且通过地址比对常量(也就是可以通过 == 直接进行比较)、效率更高。


  • #if - #esle - #endif


#ifdef __IPHONE_11_0 //对应代码 #endif 


用普通的if-else也是一样、好处就是在编译阶段是否会被编译。


不过、#if - #esle - #endif不能用来判断一个动态的语法。


  • 注册键值依赖


KVO的一个冷门方法


+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) { return [NSSet setWithObject:@"networkReachabilityStatus"];
    } return [super keyPathsForValuesAffectingValueForKey:key];
}


当return的 值被改变的时候、触发key的监听,也就是说当networkReachabilityStatus改变的时候、reachable/reachableViaWWAN/reachableViaWiFi的KVO监听都将被触发


AFSecurityPolicy


负责网络安全策略(证书)的验证模块


核心代码


  • .cer文件在iOS里如何使用的


整个验证都是基于SecTrustRef的、和.cer文件的关系大概是:


NSData格式的证书==>SecCertificateRef==>SecTrustRef对象

而SecTrustRef、就是一个内部至少携带了证书与公钥的结构体。


  • 三种验证模式


无条件信任服务器的证书、对公钥验证、对证书验证的具体逻辑。


其实整个模块也没有太多可以研究的地方、因为都是固定的方法。你只能这么写~

不过、一行一行看一看。iOS的证书到底是如何验证的、也不错。

有兴趣可以参阅《iOS源码补完计划--AFNetworking(三)》

https://www.jianshu.com/p/c1f5ec0ec459


知识点


  • __Require_Quiet判断


宏__Require_Quiet和__Require_noErr_Quiet作用其实和if-esle差不多、但是可以从多个入口跳到统一的出口、相关函数__Require_XXX基本都是这个意思。写了几个小方法、想看的自己可以copy运行一下


#import  //断言为假则会执行一下第三个action、抛出异常、并且跳到_out __Require_Action(1, _out, NSLog(@"直接跳")); //断言为真则往下、否则跳到_out __Require_Quiet(1,_out); NSLog(@"111"); //如果不注释、从这里直接就会跳到out //    __Require_Quiet(0,_out); //    NSLog(@"222"); //如果没有错误、也就是NO、继续执行 __Require_noErr(NO, _out); NSLog(@"333"); //如果有错误、也就是YES、跳到_out、并且抛出异常定位 __Require_noErr(YES, _out); NSLog(@"444");
_outNSLog(@"end"); 2018-05-17 14:18:12.656703+0800 AFNetWorkingDemo[4046:313255111 2018-05-17 14:18:12.656944+0800 AFNetWorkingDemo[4046:313255333 AssertMacros: YES == 0 ,  file: /Users/kiritoSong/Desktop/博客/KTAFNetWorkingDemo/AFNetWorkingDemo/AFNetWorkingDemo/ViewController.m, line: 39, value: 1 2018-05-17 14:18:12.657097+0800 AFNetWorkingDemo[4046:313255] end


这样、我们就有了三种判断的方式


1、普通逻辑的if-else

2、编译级别的#if - #esle - #endif

3、__Require_XXX这种多入口、统一出口的宏判断


AFHTTPRequestSerializer


负责网络请求NSMutableURLRequest对象的初始化以及请求头、请求体、参数、上传文件的自动化配置。几千行代码、很长。但是读下来会受益匪浅。


  • 流程图


AFHTTPRequestSerializer流程图


640?wx_fmt=png


流程看起来很简单、但是具体实施起来却有很多东西。


包括如何将参数字典转化成字符串并且转译、如何进行文件的分段拼接拷贝、如何将一个个请求体文件整合到request中等等。


详细的API可以参阅:《iOS源码补完计划--AFNetworking(四)》

https://www.jianshu.com/p/dfd3ae145a68


AFHTTPResponseSerializer


主要看了看AFURLResponseSerialization的内容,负责网络请求成功之后服务器返回的响应体进行格式化。


核心代码


AFURLResponseSerialization协议以及其解码方法


- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
                           data:(nullable NSData *)data
                          error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;


针对不同的解析器(JSON/XML/PList等)、通过实现这个协议的方式。


在请求结束时、帮助AFURLSessionManager对获得的响应体进行解析。


详细的API可以移步:《iOS源码补完计划--AFNetworking(五)》

https://www.jianshu.com/p/058f49013201


知识点


1、协议的应用


  • 通过让多个对象遵循同一份协议的方式、可以在解耦的时候代替继承、然后重载父类方法时通用做法。使得一个协议、返回不同的结果。

  • 在多人协作的时候、约定好协议然后交由其他业务实现、也是提升开发效率很普遍的方式。


2、如何在一个方法中返回两个NSError


可以使用嵌套的方式、比如NSUnderlyingErrorKey来指定一个最主要的错误。


3、NSIndexSet对象


NSIndexSet这个合集、是NSSet的数字版。

一个无符号整数的集合、内部元素具有唯一性。


NSMutableIndexSet *indexSetM = [NSMutableIndexSet indexSet];
[indexSetM addIndex:19];
[indexSetM addIndex:4];
[indexSetM addIndex:6];
[indexSetM addIndex:8];
[indexSetM addIndexesInRange:NSMakeRange(2010)];

[indexSetM enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { NSLog(@"%lu",idx);
}]; //2016-08-10 11:39:00.826 qikeyunDemo[3765:1000784 //2016-08-10 11:39:00.827 qikeyunDemo[3765:100078] 6 //2016-08-10 11:39:00.827 qikeyunDemo[3765:100078] 8 //2016-08-10 11:39:00.827 qikeyunDemo[3765:100078] 19 //2016-08-10 11:39:00.827 qikeyunDemo[3765:100078] 20 //2016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 21 //2016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 22 //2016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 23 //2016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 24 //2016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 25 //2016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 26 //2016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 27 //2016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 28 //2016-08-10 11:39:00.829 qikeyunDemo[3765:10007829 


内部元素会自动排序


4、服务器返回的图片是压缩过的


服务器返回的图片、需要被解压出bitmap信息。


bitmap的作用在于在将UIImage交付给UIImageView的时候。


如果没有bitmap将会在主线程自动解压一次。


640.jpeg

  • 作者:kirito_song

  • https://www.jianshu.com/p/170243957f75

  • iOS开发整理发布,转载请联系作者获得授权

640?wx_fmt=gif640?【点击成为源码大神】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值