[iOS开发]AFNetworking源码学习
Posted Billy Miracle
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[iOS开发]AFNetworking源码学习相关的知识,希望对你有一定的参考价值。
目录
简介
AFNetWorking
是使用Objective-C开发ios程序主流的网络请求开源库。
小tip:
前面的AFURLSessionManger、AFURLSessionManagerTaskDelegate、_AFURLSessionTaskSwizzling、AFHTTPSessionManager、AFSecurityPolicy、AFNetworkReachabilityManager等可作为第一部分学习,后面的UIKit+AFNetworking可作为第二部分单独学习。
开始解读
参考大佬文章
参考大佬文章
参考大佬文章
在使用时,我们要先创建一个AFHTTPSessionManager
,调用AFHTTPSessionManager
的manager
方发来进行初始化,思考一下manager
的设计模式是什么呢? 肯定有人会说是单例模式;其实manager
所用到的设计模式是工厂设计模式。
+ (instancetype)manager
return [[[self class] alloc] initWithBaseURL:nil];
- (instancetype)init //未用到
return [self initWithBaseURL:nil];
- (instancetype)initWithBaseURL:(NSURL *)url
return [self initWithBaseURL:url sessionConfiguration:nil];
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration //未用到
return [self initWithBaseURL:nil sessionConfiguration:configuration];
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
// 最终都会调用父类的初始化方法来生成对应的sessionManager
self = [super initWithSessionConfiguration:configuration];
if (!self)
return nil;
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
//为了确保NSURL +URLWithstring:relativeToURL:可以正确执行,在baseurlpath的最后添加‘/’
//url有值且没有‘/’,那么在url的未尾添加‘/’,
//如果不带‘/’会有一个默认的重定向 浪费带宽
//有没有反斜杠的意义在于该url是指向一个文件还是一个目录,说明url地址是精准表达指向的,有无'/'代表的是两个不同的地址
//没有‘/’指向的是网站目录下的一个文件
//有‘/’指向的是网站目录下的一个目录
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"])
url = [url URLByAppendingPathComponent:@""];
// 初始化URL
self.baseURL = url;
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
由此可见,在AFHTTPSessionManager
中的初始化方法最终都会调用其父类的initWitchSessionConfiguration
初始化方法,返回一个sessionManager
;那么,需要去看一下父类也就是AFURLSessionManager
的初始化都做了什么:
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration
self = [super init];
if (!self)
return nil;
// 如果会话配置为nil,对应初始化一个
if (!configuration)
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
self.sessionConfiguration = configuration;
// 初始化操作队列,并设置为串行队列 设置最大井发操作数为1
// 目的是想让并发的请求进行串行的回调
self.operationQueue = [[NSOperationQueue alloc] init];
// 1.⚠️
self.operationQueue.maxConcurrentOperationCount = 1;
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
// AFJSONResponseSerializer用来序列化HTTP响应,默认json解析
self.responseSerializer = [AFJSONResponseSerializer serializer];
// 初始化SSI所需要的 AFSecurityPolicy 用来保证请求的安全性
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
// AFNetworkReachabilityManager用来查看网络连接情况
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
// 2.⚠️
// 初始化可变任务字典 task 的id 作为 key,代理对象作为 value。
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
// 初始化锁& 命名
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
// 3.⚠️
// 获取所有的task,设置一遍 delegate 用来异步的获取当前session的所有未完成的task, session和task的关系是一个session里边会有多 个task
// 初始化的时候dataTasks-一般为空,这里这么做的主要目的是为了防止从后台回来,重新初始化整个session,
// 一些之前的后台请求任务,导致程序崩溃
[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;
它做了这些事:
- 初始化当前的会话配置、操作队列、锁、AFNetworkReachabilityManager、AFSecurityPolicy,请求序列化以及用来存储任务的可变任务字典等属性
- 获取当前
session
中所有未完成的task
,给它们设置一遍代理
这个方法里有三点需要注意:
- 队列的最大并发操作数设置为1,这里的并发操作数值的是回调代理的线程并发数
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
是用来将每一个请求任务和自定义的AFURLSessionManagerTaskDelegate
来建立映射的;(需要深入研究,代理和这里的关系,以及利用KVO的思想实现的相关)- 在初始化的时候获取当前
session
中的所有task
,为它们重新设置一遍代理;一般来说初始化的session
中的task
应该是为空的,这里这么做的主要目的是为了防止从后台回来的时候初始化session
,对于一些后台之前的请求任务没有重设代理导致崩溃的问题;这里不同的任务调用不同的addDelegateForXXX
方法来设置代理
对于使用串行队列(并发操作数设置为1),解释是:
An operation queue for scheduling the delegate calls and completion handlers. The queue should be a serial queue, in order to ensure the correct ordering of callbacks. If nil, the session creates a serial operation queue for performing all delegate method calls and completion handler calls.
用于计划委托调用和完成处理程序的操作队列。该队列应为串行队列,以确保回调的正确顺序。如果为 nil,会话将创建一个串行操作队列,用于执行所有委托方法调用和完成处理程序调用。
下面看一下这几个addDelegateForXXX
方法:
//为data任务添加代理
- (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
// 生成局部变量delegate,弱持有AFURLSessionManager对象
// 设置其manager以及completionHandler
// 由于这里是弱持有AFURLSessionManager对象,所以不会导致循环引用
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
// 设置回话任务的任务描述
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
// 调用setDelegate存储datatask
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
//为upload任务添加代理
- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:uploadTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
uploadTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:uploadTask];
delegate.uploadProgressBlock = uploadProgressBlock;
//为download任务添加代理
- (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;
这三个方法都是为不同的任务设置代理,最终都会调用setDelegate
设置代理并存储datatask
任务:
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
// 以key-value形式存储task到mutableTaskDelegatesKeyedByTaskIdentifier中
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
// 添加监听
[self addNotificationObserverForTask:task];
[self.lock unlock];
这个方法主要是把代理和task
建立映射关系,并且存储到字典当中,同时为当前task
添加监听。在添加监听的方法中,taskDidResume
和taskDidSuspend
为接收到通知后的处理方法,用来恢复任务和暂停任务。这里,对于初始化的时候获取当前session
中的所有task
,已经为它们重新设置一遍代理。回到initWitchSessionConfiguration
方法中返回当前对象,向上返回,生成AFHTTPSessionManager *sessionManger
对象。
AFHTTPSessionManager
对象有许多的接口,我们常用的很多接口,比如:
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
constructingBodyWithBlock:(nullable void (^)(id<AFMultipartFormData> _Nonnull))block
success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure;
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure;
.
.
.
很多我们常使用的GET、POST接口(有一些不是),底层都调用了:
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure;
方法,下面看看实现:
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
// 序列化错误
NSError *serializationError = nil;
// 设置request相关属性&参数,(requestSerializer:请求序列化器)
// 1.调用请求序列化类中的requestWithMethod方法进行序列化处理
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
//序列化失败的回调处理
if (serializationError)
if (failure)
//completionQueue 如果设置了这个GCD的queue,那么从这个completionQueue中回调结果就好
// 否则就从主队列回调
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^
failure(nil, serializationError);
);
return nil;
// NSURLSessionManager创建一个datatask
// 2.调用dataTaskWithRequest来生成一个datatask任务
__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;
先看看调用请求序列化类中的requestWithMethod
方法进行序列化处理:
通过requestSerializer
来调用requestWithMethod
对请求参数进行序列化,最终生成一个最终请求网络需要的request
实例;这里想一想:为什么要进行序列化?下面,先看看requestWithMethod
方法内部做了些什么吧。
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
// 判断参数是否存在 url 转化
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
// 利用url创建NSMutableURLRequest井设置http请求方法
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
// 循环遍历mutableRequest
// 在self.mutableObservedChangedKeyPaths根据keypath取出对应的value值,
// 存到创建的mutableRequest中
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths())
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath])
//通过KVC赋值
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
// 对mutableRequest参数做编码井且重新赋值给mutableRequest
// 调用requestBySerializingRequest方法将传入的parameters参数进行编码,井添加到request中
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
在requestWitchMethod
方法中,做了三件事情:
- 创建
mutableRequest
并设置其请求方法 - 把当前类设置的一些属性设置给
mutableRequest
- 把需要传递的参数进行编码并且设置到
mutableRequest
当中
下面看一下编码的代理方法:
#pragma mark - AFURLRequestSerialization
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
// 从自己持有的HTTPRequestHeaders中遍历,如果有值,就设置给mutableRequest的head
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop)
if (![request valueForHTTPHeaderField:field])
[mutableRequest setValue:value forHTTPHeaderField:field];
];
//把网络请求的参数转换为NSString类型
NSString *query = nil;
if (parameters)
if (self.queryStringSerialization)
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &serializationError);
if (serializationErroriOS 网络资源汇总之网络
对比iOS网络组件:AFNetworking 和 ASIHTTPRequest