源码分析之AFNetworking ①AFURLSessionManager与AFHTTPSessionManager
Posted 梦想家-mxj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码分析之AFNetworking ①AFURLSessionManager与AFHTTPSessionManager相关的知识,希望对你有一定的参考价值。
首先查看afn的结构,如下:

可以看到AF分为如下5个功能模块:
-
网络通信模块(最核心)(AFURLSessionManager、AFHTTPSessionManager)
-
网络状态监听模块(Reachability)
-
网络通信安全策略模块(Security)
-
网络通信信息序列化/反序列化模块(Serialization)
-
对于ios UIkit库的拓展(UIKit)
一、1.网络通信模块-AFURLSessionManager与AFHTTPSessionManager
AFHTTPSessionManager是继承AFURLSessionManager的,相当于对AFURLSessionManager的再次封装。
(1)AFURLSessionManager
1>通过查看AFURLSessionManager类:

发现AFURLSessionManager遵守NSSecureCoding, NSCopying两个协议,以及遵守
NSURLSessionDelegate,NSURLSessionTaskDelegate,NSURLSessionDataDelegate,NSURLSessionDownloadDelegate四个代理。在AFURLSessionManager中实现协议里的方法,用来处理网络请求中不同的情况:例如:暂停,取消,数据保存,更新数据进度条一样。
2>下面是查看AFURLSessionManager类,所包含的属性:

3>下面是AFURLSessionManager的方法
①

如果入参configuration为nil,则调用NSURLSessionConfiguration的defaultSessionConfiguration方法,创建一个会话配置,并使用该配置创建一个会话对象,同时还初始化了安全策略、锁、返回数据解析器(JSON 数据解析器)等属性。
②

此方法是取消会话Session,发现cancelPendingTasks是一个BOOL类型,如果返回NO,意思是允许会话中的任务执行完毕后,再取消会话,但是会话一经取消将无法重启;反之如果返回YES,那么直接取消会话,其相关联的任务和回调都将会释放。
③

再看一下实现方法:
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
创建数据服务,这里在使用会话对象 session 和入参 request 创建任务时,如果 NSFoundationVersionNumber 的值小于 NSFoundationVersionNumber_iOS_8_0 那么 dataTask 的创建会放在 af_url_session_manager_creation_queue 串行队列中同步执行,否则就由当前线程执行。接着,会调用这样的方法:
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
//加锁
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[self addNotificationObserverForTask:task];
//解锁
[self.lock unlock];
}
- (void)addNotificationObserverForTask:(NSURLSessionTask *)task {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task];
}
在这个方法中会创建一个AFURLSessionManagerTaskDelegate对象,设置其相关联的管理器,任务描述以及回调等苏醒,还会将当前会话注册为监听者,监听 task 任务发出的 AFNSURLSessionTaskDidResumeNotification 和 AFNSURLSessionTaskDidSuspendNotification 通知。当接收到该通知后,分别执行 taskDidResume: 和 taskDidSuspend: 方法,在这两个方法中又发出了 AFNetworkingTaskDidResumeNotification 和 AFNetworkingTaskDidSuspendNotification 通知。
④上传
使用NSURLSessionUploadTask进行文件上传,有三种方式:
NSData对象上传
-(NSURLSessionUploadTask )uploadTaskWithRequest:(NSURLRequest )request fromData:(NSData *)bodyData;
文件上传
-(NSURLSessionUploadTask )uploadTaskWithRequest:(NSURLRequest )request fromFile:(NSURL *)fileURL;
流上传
-(NSURLSessionUploadTask )uploadTaskWithStreamedRequest:(NSURLRequest )request;

此方法是创建文件上传任务
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromFile:(NSURL *)fileURL
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
if (uploadTask) {
[self addDelegateForUploadTask:uploadTask
progress:uploadProgressBlock
completionHandler:completionHandler];
}
return uploadTask;
}
如果果后台会话对象创建文件上传任务失败时,会根据条件尝试重新创建,当然 AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask 为 5 ,所以只能尝试 5次。如果任务创建成功,则进而为任务创建一个 AFURLSessionManagerTaskDelegate 对象,作为任务的代理。请求报文的请求体数据即为根据参数 fileURL 获取的文件数据。
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromData:(NSData *)bodyData
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
__block NSURLSessionUploadTask *uploadTask = nil;
url_session_manager_create_task_safely(^{
uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
});
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler]; return uploadTask;
}
此方法上传数据与上面上传文件类似,待上传的数据直接由参数 bodyData 给出。
数据流上传,实现
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithStreamedRequest:request];
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
return uploadTask;
}
例子:
NSURL *textFileURL = [NSURL fileURLWithPath:@"/path/to/file.txt"];
NSURL *url = [NSURL URLWithString:@"https://www.example.com/"];
NSMutableURLRequest *mutableRequest = [NSMutableURLRequest requestWithURL:url];
mutableRequest.HTTPMethod = @"POST";
mutableRequest.HTTPBodyStream = [NSInputStream inputStreamWithFileAtPath:textFileURL.path];
[mutableRequest setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
[mutableRequest setValue:[NSString stringWithFormat:@"%lld", data.length] forHTTPHeaderField:@"Content-Length"];
NSURLSessionUploadTask *uploadTask = [defaultSession uploadTaskWithStreamedRequest:mutableRequest];
[uploadTask resume];
上传任务可以分三种方式上传,如果你的文件小,不怎么占内存,可以使用NSData创建上传任务,否则请使用文件创建上传任务。如果想边生产数据边上传的话,请使用流。
⑤下载:

实现:
- (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
{
NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request];
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
return downloadTask;
}
创建下载任务:
request创建任务时使用的请求报文头信息
downloadProgressBlock下载进度更新时调用的代码块,这个代码会在会话队列中调用,所以如果更新视图,需要自己在任务队列中指定主队列
destination 任务下载结束后,该参数可以返回指定的文件保存地址,缓存数据被移动到该地址,里面的targetPath为下载的数据缓存地址
completionHandler 下载任务结束后的回调
进一步调用
- (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;
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;
}
另一个下载api
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
progress:(NSProgress * __autoreleasing *)progress
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
__block NSURLSessionDownloadTask *downloadTask = nil;
dispatch_sync(url_session_manager_creation_queue(), ^{
downloadTask = [self.session downloadTaskWithResumeData:resumeData];
});
[self addDelegateForDownloadTask:downloadTask progress:progress destination:destination completionHandler:completionHandler]; return downloadTask;
}
创建重用数据的下载任务:使用已经下载的部分数据resumeData创建一个下载任务,继续进行下载。
可用于断点下载
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
这个方法可以讲当前的task已下载的数据使用一个data保存起来, 然后使用NSURLSession的方法可以根据这个data从服务器获取后续的数据
例子:
+ (NSURLSessionDownloadTask *)downloadFileWithUrl:(NSString *)url DownloadProgress:(DownloadProgress)progress DownloadCompletion:(CompletionState)completion{
// 1、 设置请求
NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
// 2、初始化
NSURLSessionConfiguration * configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager * manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
// 3、开始下载
NSURLSessionDownloadTask * task = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
progress(1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount,1.0 * downloadProgress.totalUnitCount,1.0 * downloadProgress.completedUnitCount);
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
//这里要返回一个NSURL,其实就是文件的位置路径
NSString * path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//使用建议的路径
path = [path stringByAppendingPathComponent:response.suggestedFilename];
NSLog(@"%@",path);
return [NSURL fileURLWithPath:path];//转化为文件路径
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
//如果下载的是压缩包的话,可以在这里进行解压
NSLog(@"----%@---%d---%@",error.domain,error.code,error);
//下载成功
if (error == nil) {
completion(YES,@"下载完成",[filePath path]);
}else{//下载失败的时候,只列举判断了两种错误状态码
NSString * message = nil;
if (error.code == - 1005) {
message = @"网络异常";
}else if (error.code == -1001){
message = @"请求超时";
}else{
message = @"未知错误";
}
completion(NO,message,nil);
}
}];
return task;
}
⑥
- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task;
获取任务数据的上传进度
⑦
- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task;
获取任务数据的下载进度
AFURLSessionManager 中实现的代理方法
AFURLSessionManager 遵循 NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate 协议,以处理网络请求过程中的数据。
有些代理方法中所做的任务,完全由 AFURLSessionManager 的代码块属性决定。如果这些属性并没有设置,那么相应的代理方法就没必要响应。所以 AFURLSessionManager 中重写了 respondsToSelector: 过滤了一些不必响应的代理方法。
NSURLSessionDownloadDelegate
当下载任务结束后,调用的代理方法:
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
//获取与任务对应的代理对象
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
//如果设置了回调任务,先执行回调任务
if (self.downloadTaskDidFinishDownloading) {//获取下载数据的地址
NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (fileURL) {
delegate.downloadFileURL = fileURL;
NSError *error = nil;
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {//移动下载的数据到指定的地址
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil];
}
return;
}
}
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
}
}
从上面的代码可知,如果会话管理器的 downloadTaskDidFinishDownloading 的代码块返回了地址,那么便不会去执行任务本身所对应的代理方法了,并且如果移动文件失败便会推送一个 AFURLSessionDownloadTaskDidFailToMoveFileNotification 通知。
下面两个协议方法中,都是先执行任务所关联的代理对象的方法,再执行会话对象设置的 downloadTaskDidWriteData 或 downloadTaskDidResume 任务。
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;
self.downloadProgress.completedUnitCount = totalBytesWritten;
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes{
self.downloadProgress.totalUnitCount = expectedTotalBytes;
self.downloadProgress.completedUnitCount = fileOffset;
}
从这些代理方法中可知,设置 AFURLSessionManager 会话实例对象的代码块任务属性,那么这些回调任务对于每一个网络请求任务都是有效的,所以针对于单个特殊的任务回调操作,便不能放在会话管理器的属性中,而是要放在与任务相关联的 AFURLSessionManagerTaskDelegate 代理对象中。
实际使用 AFURLSessionManager 的方法创建网络请求任务时,传递的回调任务,都是在与任务相关联的代理对象的方法中执行的。
以上就是AFURLSessionManager的内容。下面讲解他的子类:AFHTTPSessionManager
(2)AFHTTPSessionManager
发现AFHTTPSessionManager是继承AFURLSessionManager并遵守<NSSecureCoding, NSCopying>
成员属性:
/**
The URL used to construct requests from relative paths in methods like requestWithMethod:URLString:parameters:
, and the GET
/ POST
/ et al. convenience methods.
/
@property (readonly, nonatomic, strong, nullable) NSURL baseURL;
/
Requests created with requestWithMethod:URLString:parameters:
& multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:
are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of AFHTTPRequestSerializer
, which serializes query string parameters for GET
, HEAD
, and DELETE
requests, or otherwise URL-form-encodes HTTP message bodies.
@warning requestSerializer
must not be nil
.
/
@property (nonatomic, strong) AFHTTPRequestSerializer * requestSerializer;
/*
Responses sent from the server in data tasks created with dataTaskWithRequest:success:failure:
and run using the GET
/ POST
/ et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of AFJSONResponseSerializer
.
@warning responseSerializer
must not be nil
.
*/
@property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer;
/**
The security policy used by created session to evaluate server trust for secure connections. AFURLSessionManager
uses the defaultPolicy
unless otherwise specified. A security policy configured with AFSSLPinningModePublicKey
or AFSSLPinningModeCertificate
can only be applied on a session manager initialized with a secure base URL (i.e. https). Applying a security policy with pinning enabled on an insecure session manager throws an Invalid Security Policy
exception.
*/
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
方法:
+ (instancetype)manager;
- (instancetype)initWithBaseURL:(nullable NSURL *)url;
- (instancetype)initWithBaseURL:(nullable NSURL *)url
sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration;
请求方式:
GET 请求是向服务端发起请求数据,用来获取或查询资源信息
POST 请求是向服务端发送数据的,用来更新资源信息,它可以改变数据的种类等资源
PUT 请求和POST请求很像,都是发送数据的,但是PUT请求不能改变数据的种类等资源,它只能修改内容
DELETE 请求就是用来删除某个资源的
PATCH 请求和PUT请求一样,也是用来进行数据更新的,它是HTTP verb推荐用于更新的
在实际开发过程中,我们还是使用【GET 和 POST】请求是最多的。
GET请求
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
progress:(nullable void (^)(NSProgress * _Nonnull))downloadProgress
success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
//调用另一个方法构造GET请求
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
headers:headers
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
/*
启动任务
使用AFHTTPSessionManager创建的任务默认都帮你启动了,所以不需要手动调用resume方法了。
上面说的AFURLSessionManager默认没有启动,所以获取任务后要手动启动 */
[dataTask resume];
return dataTask;
}
进一步调用
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
for (NSString *headerField in headers.keyEnumerator) {
[request setValue:headers[headerField] forHTTPHeaderField:headerField];
}
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
return dataTask;
}
同理post请求
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters headers:headers uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];
[dataTask resume];
return dataTask;
}
以上是关于源码分析之AFNetworking ①AFURLSessionManager与AFHTTPSessionManager的主要内容,如果未能解决你的问题,请参考以下文章
源码分析之AFNetworking ①AFURLSessionManager与AFHTTPSessionManager
源码分析之AFNetworking④UIkit+AFNetworking
源码分析之AFNetworking④UIkit+AFNetworking
源码分析之AFNetworking④UIkit+AFNetworking