参拜一下AFNetworking的源码。
第四篇源码、暂时来看也是iOS方向的最后一篇、撸完准备趁着热乎撸一撸网络协议。
目录
准备工作
功能模块
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协议以及其解码方法
核心代码
知识点
参考资料
准备工作
GitHub
使用版本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/NSURLSessionDEPENDENCIES: - AFNetworking SPEC CHECKSUMS: AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67 PODFILE CHECKSUM: 75e1e619317fd130ee494d35ddff3d9c614c4390 COCOAPODS: 1.3.1
推荐在看AFN之前、先了解一下NSURLSession
不然感觉会看的一头雾水、也体会不到AFN的伟大之处
《iOS基础深入补完计划--网络模块NSURLSession概述》
功能模块
除了这四个服务性模块之外、UIKit文件夹下基本是对各种UI控件的扩展。
AFURLSessionManager/AFHTTPSessionManager
AFURLSessionManager流程
承接了主要的网络传输任务、实现了NSURLSession绝大部分的代理方法。
核心代码
一些比较有意思的东西
在监听属性的时候、可以用
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 (readonly, nonatomic, strong, nullable) NSURL *baseURL; .m中@property (readwrite, nonatomic, strong) NSURL *baseURL;
AFNetworkReachabilityManager
AFN中负责网络状态模块。在不同的网络状态下可以监听、或者实时查询、并且需要手动开启或者关闭。
四种网络状态
未知、无网络、运营商网络、WiFi网络
开始暂停
状态改变的回调block
代码不多、详情可参阅《iOS源码补完计划--AFNetworking(二)》
知识点
关于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
、就是一个内部至少携带了证书与公钥的结构体。
三种验证模式
无条件信任服务器的证书
、对公钥验证
、对证书验证
的具体逻辑。后两种需要我们本地自建证书(由服务器提供原始证书生成)。
如果不需要验证什么、压根不需要设置AFSecurityPolicy
、因为在manager的初始化里已经默认了一个AFSecurityPolicy
并且被设置成无条件信任服务器的证书
。当然、这样你的HTTPS除了加密通道意外将毫无用处、而这个通道、也是可以被抓包的。
其实整个模块也没有太多可以研究的地方、因为都是固定的方法。你只能这么写~
不过、一行一行看一看。iOS的证书到底是如何验证的、也不错。
有兴趣可以参阅《iOS源码补完计划--AFNetworking(三)》
知识点
__Require_Quiet判断
宏__Require_Quiet
和__Require_noErr_Quiet
作用其实和if-esle
差不多、但是可以从多个入口跳到统一的出口、相关函数__Require_XXX
基本都是这个意思。写了几个小方法、想看的自己可以copy运行一下
#import <AssertMacros.h> //断言为假则会执行一下第三个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"); _out: NSLog(@"end");2018-05-17 14:18:12.656703+0800 AFNetWorkingDemo[4046:313255] 1112018-05-17 14:18:12.656944+0800 AFNetWorkingDemo[4046:313255] 333AssertMacros: YES == 0 , file: /Users/kiritoSong/Desktop/博客/KTAFNetWorkingDemo/AFNetWorkingDemo/AFNetWorkingDemo/ViewController.m, line: 39, value: 12018-05-17 14:18:12.657097+0800 AFNetWorkingDemo[4046:313255] end
这样、我们就有了三种判断的方式
1、普通逻辑的if-else
2、编译级别的#if - #esle - #endif
3、__Require_XXX这种多入口、统一出口的宏判断
AFHTTPRequestSerializer
负责网络请求NSMutableURLRequest对象的初始化
以及请求头、请求体、参数、上传文件的自动化配置
几千行代码、很长。但是读下来会受益匪浅。
流程图
AFHTTPRequestSerializer流程图
流程看起来很简单、但是具体实施起来却有很多东西。
包括如何将参数字典转化成字符串并且转译、如何进行文件的分段拼接拷贝、如何将一个个请求体文件整合到request中等等。
详细的API可以参阅:《iOS源码补完计划--AFNetworking(四)》
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(五)》
知识点
协议的应用
通过让多个对象遵循同一份协议的方式、可以在解耦的时候代替继承、然后重载父类方法时通用做法。使得一个协议、返回不同的结果。
在多人协作的时候、约定好协议然后交由其他业务实现、也是提升开发效率很普遍的方式。
如何在一个方法中返回两个NSError
可以使用嵌套的方式、比如NSUnderlyingErrorKey
来指定一个最主要的错误。
NSIndexSet对象
NSIndexSet这个合集、是NSSet的数字版。
一个无符号整数的集合、内部元素具有唯一性。
NSMutableIndexSet *indexSetM = [NSMutableIndexSet indexSet]; [indexSetM addIndex:19]; [indexSetM addIndex:4]; [indexSetM addIndex:6]; [indexSetM addIndex:8]; [indexSetM addIndexesInRange:NSMakeRange(20, 10)]; [indexSetM enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { NSLog(@"%lu",idx); }];//2016-08-10 11:39:00.826 qikeyunDemo[3765:100078] 4//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:100078] 29
作者:kirito_song
链接:https://www.jianshu.com/p/170243957f75
共同学习,写下你的评论
评论加载中...
作者其他优质文章