AFNetworking 源码解读
最近开始看第三方库优秀源码的计划,这是第一个,AFNetworking来和大家分享一下。
AFNetworking 是一个十分优秀的网络框架,简单易用。 在开始之前,最好先了解一下NSURLSession相关的知识。详情可看http://www.cnblogs.com/bigly/p/8476610.html。
首先,我们来看一下AFNetworking的文件结构:
从图中我们可以看出,AF大致分为五个模块:
网络请求模块, (AFURLSessionManager, AFHTTPSessionManager)
网络状态模块, (AFNetworkReachabilityManager)
安全策略模块, (AFSecurityPolicy)
序列化反序列化模块,(AFURLRequestSerialization, AFURLResponseSerialization)
UI相关模块
其中,最重要的是网络请求模块。
现在我们来看看AFNetworking怎么使用的,以下是作者git上的源代码:
网络请求:
下载:
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL]; NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
NSLog(@"File downloaded to: %@", filePath);
}];
[downloadTask resume];
通过这段代码,我们可以看到,AFNetworking 是通过对NSURLSession进行封装来实现的。我们先来看一下AFURLSessionManager的初始化,在初始化方法里AF并没有做太多事,主要是NSURLSession的初始化以及安全策略等初始化。(关于安全策略和序列化我们后面再进行讲解)
- (instancetype)init {
return [self initWithSessionConfiguration:nil];
} - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
} if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
} self.sessionConfiguration = configuration; //初始化任务所在队列,且设置并发数为1
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = ; //初始化NSURLSession
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; //初始化Serializer
self.responseSerializer = [AFJSONResponseSerializer serializer]; //初始化安全策略
self.securityPolicy = [AFSecurityPolicy defaultPolicy]; //初始化网络监听模块
#if !TARGET_OS_WATCH
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif //初始化代理相关的字典
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init]; //初始化锁
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName; /*初始化时获取所有进行中的任务,并将所有代理置空。 正常来说,初始化时应该获取不到任何的task, 事实上也是如此,但在
进入后台再到前台初始化化时可能会有任务,导致崩溃
*/ [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
for (NSURLSessionDataTask *task in dataTasks) {
[self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
} for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
[self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
} for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
[self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
}
}]; return self;
}
接下来看一下NSURLSessionDownloadTask 的获取方法。
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
__block NSURLSessionDownloadTask *downloadTask = nil; //同步队列生成task
url_session_manager_create_task_safely(^{
downloadTask = [self.session downloadTaskWithRequest:request];
}); //添加对应的AFURLSessionManagerTaskDelegate
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler]; return downloadTask;
}
这里有一个比较巧妙的地方,AFURLSessionManager实现了NSURLSession相关的代理方法,然后在每次生成task时,将传入的block以字典形式持有,
然后在NSURLSession代理方法中调用,这样使用者就不需要去实现相关的代理,只需要传入block,更加简洁。
- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
//创建代理对象
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];
delegate.manager = self; //设置对应的block
delegate.completionHandler = completionHandler; if (destination) {
delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
return destination(location, task.response);
};
} downloadTask.taskDescription = self.taskDescriptionForSessionTasks; //将代理对象添加到字典中
[self setDelegate:delegate forTask:downloadTask]; delegate.downloadProgressBlock = downloadProgressBlock;
}
此外,在添加代理对象的时候,AF增加了一些额外的监听方法,使用rutime实现,实现方式比较巧妙,有兴趣的可以研究一下
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate); [self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
//监听task,使用runtime来实现
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
获取到NSURLSessionDownloadtask之后直接调用resume方法开始下载,接下来我们看看回调相关的方法。
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
//从字典获取对应的代理对象
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; //如果设置了downloadTask处理的block则执行该block
//一般比较少采用这种方式
if (self.downloadTaskDidFinishDownloading) { //获取文件保存路径
NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (fileURL) {
delegate.downloadFileURL = fileURL;
NSError *error = nil; //对于downloadtask,下载完成后需要将文件拷贝到其他路径否则会被删除
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
} return;
}
} //使用代理对象执行对应方法,和手动设置block逻辑一样
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
}
}
在下载完成的回调方法中,我们看到有两条路径,一个是从字典中获取对应的代理对象,另外一种是直接获取下载完成回调的block,
这两种方式大同小异,都是在回调方法中做了一层转接,如果要直接使用下载完成的block可以通过以下方法设置。
- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *locatio
NSURLSessionDownloadTaskDelegate 还有很多其他回调方法,但原理同上,在此不一一赘述。
NSURLSessionDataTask,NSURLSessionUploadTask等和NSURLSessionDownLoadTask的使用类似,也不一一讲解。
关于网络请求,我们可以看到还有一个AFHTTPSessionManager, 顾名思义,这个类是专门为HTTP请求封装的。它继承自AFHTTPSessionManager
对比一下AFURLSessionManager,我们可以发现,在AFHTTPSessionManager有一个属性是AFURLSessionManager所没有的。
@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
这个类负责构造各种http请求,而实际发送请求的还是在AFURLSessionManger中的相关方法。
后续讲解AFURLRequestSerialization时会提到。//ps:先占个坑,有时间继续完成
AFNetworking 源码解读的更多相关文章
- AFNetworking 3.0 源码解读 总结(干货)(下)
承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上) 21.网络服务类型NSURLRequestNetworkServiceType 示例代码: typedef NS_ENUM(N ...
- AFNetworking 3.0 源码解读 总结(干货)(上)
养成记笔记的习惯,对于一个软件工程师来说,我觉得很重要.记得在知乎上看到过一个问题,说是人类最大的缺点是什么?我个人觉得记忆算是一个缺点.它就像时间一样,会自己消散. 前言 终于写完了 AFNetwo ...
- AFNetworking 3.0 源码解读(十一)之 UIButton/UIProgressView/UIWebView + AFNetworking
AFNetworking的源码解读马上就结束了,这一篇应该算是倒数第二篇,下一篇会是对AFNetworking中的技术点进行总结. 前言 上一篇我们总结了 UIActivityIndicatorVie ...
- AFNetworking 3.0 源码解读(十)之 UIActivityIndicatorView/UIRefreshControl/UIImageView + AFNetworking
我们应该看到过很多类似这样的例子:某个控件拥有加载网络图片的能力.但这究竟是怎么做到的呢?看完这篇文章就明白了. 前言 这篇我们会介绍 AFNetworking 中的3个UIKit中的分类.UIAct ...
- AFNetworking 3.0 源码解读(九)之 AFNetworkActivityIndicatorManager
让我们的APP像艺术品一样优雅,开发工程师更像是一名匠人,不仅需要精湛的技艺,而且要有一颗匠心. 前言 AFNetworkActivityIndicatorManager 是对状态栏中网络激活那个小控 ...
- AFNetworking 3.0 源码解读(八)之 AFImageDownloader
AFImageDownloader 这个类对写DownloadManager有很大的借鉴意义.在平时的开发中,当我们使用UIImageView加载一个网络上的图片时,其原理就是把图片下载下来,然后再赋 ...
- AFNetworking 3.0 源码解读(七)之 AFAutoPurgingImageCache
这篇我们就要介绍AFAutoPurgingImageCache这个类了.这个类给了我们临时管理图片内存的能力. 前言 假如说我们要写一个通用的网络框架,除了必备的请求数据的方法外,必须提供一个下载器来 ...
- AFNetworking 3.0 源码解读(六)之 AFHTTPSessionManager
AFHTTPSessionManager相对来说比较好理解,代码也比较短.但却是我们平时可能使用最多的类. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilit ...
- AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager
做ios开发,AFNetworking 这个网络框架肯定都非常熟悉,也许我们平时只使用了它的部分功能,而且我们对它的实现原理并不是很清楚,就好像总是有一团迷雾在眼前一样. 接下来我们就非常详细的来读一 ...
随机推荐
- pureftpd支持php实现图片上传
安装好php后,可安装pureftp工具 这里我给出一个rpm包可直接安装配置:http://pan.baidu.com/s/1i5OhS3r(包括启动脚本在内) FTP测试:安装ftp客户端,用户名 ...
- [css 实践篇] 解决悬浮的<header> <footer>遮挡内容的处理技巧
我写的实践篇 都是自己在实践项目所遇到的 "拦路虎" 还是很有借鉴的意义的.(实践才是检验真理的唯一标准呀),废话不多说,进去正题 position: fixed 绝对固定底部后会 ...
- Linux下安装配置jdk
步骤: 1.去官网下载jdk压缩包 网址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151. ...
- Matlab绘图基础——绘制三维表面
%绘制三维表面 ------------------------------------- %1.绘制线框图:mesh:每一条曲线称为mesh line %首先利用meshgrid函数产生平面区域内的 ...
- Jmeter中正则表达式提取器使用详解
在使用Jmeter过程中,会经常使用到正则表达式提取器提取器,虽然并不直接涉及到请求的测试,但是对于数据的传递起着很大的作用,本篇博文就是主要讲解关于正则表达式及其在Jmeter的Sampler中的调 ...
- 分享:docker swarm集群搭建
[Y_H]实践原创 三台虚拟机:1台centOS , 2台ubuntu. 网上有用docker-machine创建虚拟机做的例子. 这里直接用VMware创建这三台虚拟机,然后用xshell连 ...
- <经验杂谈>介绍Js简单的递归排列组合
最近在开发SKU模块的时候,遇到这样一个需求,某种商品有N(用未知数N来表示是因为规格的数组由用户制定且随时可以编辑的,所以对程序来说,它是一个未知数)类规格,每一类规格又有M个规格值,各种规格值的组 ...
- New UWP Community Toolkit - Carousel
概述 New UWP Community Toolkit V2.2.0 的版本发布日志中提到了 Carousel 的调整,本篇我们结合代码详细讲解 Carousel 的实现. Carousel 是 ...
- 第二篇:Python数据类型
一.引子 1.什么是数据? x= #是我们要存储的数据 2.为何数据要分不同的类型 数据是用来表示状态的,不同的状态就应该用不同的类型的数据去表示 3.数据类型 数字(整型,长整型,浮点型,复数) 字 ...
- python 面向对象之封装与类与对象
封装 一,引子 从封装本身的意思去理解,封装就好像是拿来一个麻袋,把小猫,小狗,小王八,小老虎一起装进麻袋,然后把麻袋封上口子.照这种逻辑看,封装='隐藏',这种理解是相当片面的 二,先看如何隐藏 在 ...