AFNetworking引言:
iOS网络请求,在iOS7以前,使用NSURLConnection,随着iOS7的发布,出现了NSURLSession处理网络请求.我们开发中使用的三方SDK,如AFNetworking、SDWebImage等等都使用了NSURLSession,都是对它的封装!
本文将从AFNetworking源码入手,简单的探索一下他的实现原理:(iOS小白,请多关照)
AFNetworking版本3.2.1
AFNetworking结构图
网络请求时,我们使用AFHTTPSessionManager来创建网络请求,它继承自AFURLSessionManager类,看AFHTTPSessionManager类实现我们可以看出,这个类只是做了逻辑的分发工作,网络请求的逻辑给了他的父类AFURLSessionManager以及其他的类去做!
//先来一个网络请求
/**
初始化manager:
AFHTTPSessionManager中,
+ (instancetype)manager;
- (instancetype)initWithBaseURL:(nullable NSURL *)url;
- (instancetype)initWithBaseURL:(nullable NSURL *)url sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration
以及类中的init对象方法,都会调用到本类中的:- (instancetype)initWithBaseURL:(nullable NSURL *)url sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration初始化方法
*/
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc]init];
[manager GET:@"https://www.baidu.com" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
下面我们来看AFHTTPSessionManager中的initWithBaseURL:sessionConfiguration:方法都做了什么事情
- (instancetype)initWithBaseURL:(NSURL *)url sessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
//有道翻译:确保baseURL路径的终端斜线,以便NSURL +URLWithString:relativeToURL:按预期工作
//判断传入的url是否存在且是否是以"/"结尾
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url;//保存url
//初始化请求序列化对象和响应序列化对象
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
/**
从该方法的实现可以看出,该方法只是调用了父类的初始化方法,保存了url以及初始化了两个序列化对象.
*/
现在,我们去看父类的初始化方法做了些什么吧…
- (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;
self.operationQueue = [[NSOperationQueue alloc] init];
//回调队列的并发数
self.operationQueue.maxConcurrentOperationCount = 1;
//初始化session,并设置代理
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
self.responseSerializer = [AFJSONResponseSerializer serializer];
//安全策略
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
//mutableTaskDelegatesKeyedByTaskIdentifier:字典,存储task唯一对应的mutableTaskDelegatesKeyedByTaskIdentifier代理,通过这个唯一对应来处理相对应的代理方法.
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
//初始化对象锁,用来处理多线程对可变字典mutableTaskDelegatesKeyedByTaskIdentifier操作时的线程安全
//
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
//这个获取session对应的未完成的task,那么在初始化时,获取的数组应该是空的才对.
//不甚理解,后来在学习过程中,看到一个大神说的,当程序从后台回到前台,防止重新初始化先前后台未完成的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;
}
我们完成了初始化之后,下面来看看请求方法
- (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 {
//调用对象方法创建task
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
//开始请求
[dataTask resume];
return dataTask;
}
//AFHTTPSessionManager中生成task任务的对象方法
- (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 {
/**
此处是将Method&url¶meters等参数转化成一个NSMutableURLRequest
*/
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
if (serializationError) {
if (failure) {
//如果有completionQueue,则从该队列回调failure;没有completionQueue,则从主队列回调
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__block NSURLSessionDataTask *dataTask = nil;
//请求父类方法,最终生成任务task实例,并且在completionHandler中回调传入的failure和success.
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;
}
/**
此方法里首先是使用requestSerializer生成request;
然后根据request调用父类的方法生成最终的task.
*/
接下来我们首先看看生成request的方法的实现
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error {
//断言,debug时,缺少这两个参数会在此处crash.
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
//根据URLString生成url,如果url不存在,则crash
NSParameterAssert(url);
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
/**
AFHTTPRequestSerializerObservedKeyPaths() C函数,
存储了需要观察的path:allowsCellularAccess, cachePolicy, HTTPShouldHandleCookies, HTTPShouldUsePipelining, networkServiceType, timeoutInterval;
*/
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
/**
mutableObservedChangedKeyPaths:集合,
AFHTTPRequestSerializer的一个属性,在AFHTTPRequestSerializer对象init时,
创建了mutableObservedChangedKeyPaths这个集合
并且为AFHTTPRequestSerializerObservedKeyPaths()该方法获取到的属性名添加了观察者
此为集合初始化及添加观察者:
self.mutableObservedChangedKeyPaths = [NSMutableSet set];
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
}
}
//观察者:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(__unused id)object change:(NSDictionary *)change
context:(void *)context {
if (context == AFHTTPRequestSerializerObserverContext) {
//观察者监听到属性的set方法被调用后,先判断属性值是否为null,为null则移除,否则添加到集合中
if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
[self.mutableObservedChangedKeyPaths removeObject:keyPath];
} else {
[self.mutableObservedChangedKeyPaths addObject:keyPath];
}
}
}
*/
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
//此时,将自己监听到设置过的属性值赋值给Request
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
/**
requestBySerializingRequest:withParameters:error:该方法将传入的parameters参数编码,拼接到request中
涉及到parameters参数的解析方式以及根据请求方式对请求体的设置.
*/
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
/**
C函数,存储了NSUrlRequest的属性名,
在上面的方便中,遍历这个数组,监听这些属性值的改变,并且对应的给这些属性赋值
*/
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
});
return _AFHTTPRequestSerializerObservedKeyPaths;
}
以上,我们就获取到了request,接下来我们看核心类AFURLSessionManger的生成task任务的方法
- (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 {
__block NSURLSessionDataTask *dataTask = nil;
//对iOS8.0以下的线程安全处理
//使用同步任务串行队列确保传值正确,并且创建的taskIdentifiers值唯一,后面要使用taskIdentifiers与delegate唯一对应.
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
static void url_session_manager_create_task_safely(dispatch_block_t block) {
if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
// Fix of bug
// Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
// Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093
dispatch_sync(url_session_manager_creation_queue(), block);
} else {
block();
}
}
static dispatch_queue_t url_session_manager_creation_queue() {
static dispatch_queue_t af_url_session_manager_creation_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
});
return af_url_session_manager_creation_queue;
}
//接下来设置task与delegate关联
- (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];
//代理中拥有AFURLSessionManager属性
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);
//加锁,保证线程安全,mutableTaskDelegatesKeyedByTaskIdentifier为可变字典.
[self.lock lock];
//用taskIdentifier与delegate建立对应关系,且taskIdentifier唯一
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
//发送任务开启暂停的通知
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
到这里,任务创建成功,并且与代理建立对应关系,然后task开始请求网络,还记得我们初始化方法中的:self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];是的,我们又回到了这里,在此处,我们把self(即AFUrlSessionManager)设置为task的delegate,请求开始后,就会调用NSURLSession的相关代理,AFURLSessionManager的声明中:@interface AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>,AFURLSessionManager遵守了NSURLSession的代理.
剩下的就是一堆代理的回调以及AFNetworking自定义的回调请求结果的block,看着晕,麻烦自行查看啦!谢谢!