iOS开发——AFNetworking源码(一)

简介

我们在学习AFNetworking之前,就必须知道AFNetworking是什么,具体有什么作用?
首先,AFNetworking是一个功能非常强大的框架,主要是用于网络请求,把复杂的原生代码封装好,我们只需要简单的几步就可以完成网络请求。

代码组成

ios Foundation 源码 ios 开发源码_ios

从上图可以看出AFNetworking中含有很多类,那么这些类与类之间有什么关系呢,具体关系如图所示:

ios Foundation 源码 ios 开发源码_网络_02

我们来解读一下这个图,按照我的理解来说,显而易见的是AFURLSessionManager应该是主力军,因为左边黄色框框里的四个类都是服务于AFURLSessionManager,举个例子,AFURLSessionManager如果是前方部队的话,其余四个就是他的后备军,所以只有AFURLSessionManager管理好这四个类,它才可以好好工作。(也就是说生成对应的sessionmanger,并且初始化并管理AFSecurityPolicy、AFNetworkReachabilityManager以及AFJSONResponseSerializer来一同为它所生成的sessionmanger服务)。

而AFHTTPSessionManager是继承于AFURLSessionManger的子类,内部管理自己的AFHTTPResponseSerializer和AFHTTPRequestSerializer两种序列化工具,用来对请求和响应的数据来做序列化;同时依赖于父类提供的保证安全,监控网络状态,实现发出HTTP请求的核心功能;AFHTTPSessionManager本身是对网络请求做了一些简单的封装,请求的整个逻辑是分发给AFURLSessionManager或者其他类去做的;

模块划分

AFNetworking可以分成五个部分,具体如下:

  1. 网络通信模块(AFURLSessionManager、AFHTTPSessionManager)
  2. 网络状态监听模块(AFNetworkReachabilityManager)
  3. 网络通信安全策略模块(AFSecurityPolicy)
  4. 网络通信信息序列化模块(AFURLRequestSerialization,AFURLResponseSerialization)
  5. iOS UIkit库的拓展(UIKit)

我们可以看到AFNetWorking大致分为一下五大模块,其中最为核心的是网络通信模块,主要用来发送和响应请求;AFNetworkReachabilityManager主要是用来进行网络状态的监听,在不同网络状态下请求和响应的不同操作和处理;为了保证网络请求的安全性,当然少不了AFSecurityPolicy,网络安全策略模块,在初始化sessionmanger的时候同时初始化了AFSecurityPolicy以及网络通信信息请求序列化的类。网络通信信息序列化模块分为请求序列化和响应序列化,其内部分别会对请求头和响应头进行编码,在请求的时候,将请求头转码为计算机可识别的格式,在响应的时候将响应结果进行转码后传回给客户端。这几个模块息息相关,都是为一次HTTP请求的正常请求和响应做保障。最后一个模块就是基于UIKit库的一些相关拓展,其中包括UIImageView的请求,UIButton的请求等等。

AFNetworking使用流程

我们在使用AFNetworking进行网络请求的时候,首先需要生成对应的sessionManager,以我项目中的代码为例:

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

我们要是直接看源码去学习关于上面的代码干了什么会有一些困难,所以我们可以先看一下下面的图解:

ios Foundation 源码 ios 开发源码_ios_03

我们通过调用AFHTTPSessionManager的manager方法来进行初始化,那么在源码中manager方法实现了什么呢?

我们跳转到源码可以发现

ios Foundation 源码 ios 开发源码_初始化_04


由此可见,在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];
    self.operationQueue.maxConcurrentOperationCount = 1;
	//AFJSONResponseSerializer用来序列化HTTP响应
    self.responseSerializer = [AFJSONResponseSerializer serializer];
	//初始化SSI所需要的AFSecurityPolicy用来保证请求的安全性
    self.securityPolicy = [AFSecurityPolicy defaultPolicy];

#if !TARGET_OS_WATCH
	//AFNetworkReachabilityManager用来查看网络连接状态
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
	//初始化可变任务字典task的id作为key,代理对象作为value
	//2.⚠️
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
	//初始化锁和锁的命名
    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;
	//获取所有的task,设置一遍delegate用来异步的获取当前session的所有未完成的task,session和task的关系是一个session里有多个task
	//初始化dataTasks一般为空,这里这么做的主要目的是为了防止从后台回来,重新初始化整个session,一些之前的后台请求任务,导致程序崩溃
	//3.⚠️
    [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;
}

总结以下几点:

  1. 初始化当前的会话配置,操作队列,锁,AFNetworkReachabilityManager,AFSecurityPolicy,请求序列化以及用来存储任务的可变任务字典等属性;
  2. 获取当前session中所有未完成的task,给它们设置一遍代理;

这个函数有三个需要注意的点:
队列的最大并发操作数设置为1,这里的并发操作数值的是回调代理的线程并发数。

  1. self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];是用来将每一个请求任务和自定义的AFURLSessionManagerTaskDelegate来建立映射的;(需要深入研究,代理和这里的关系,以及利用KVO的思想实现的相关)

  2. 在初始化的时候获取当前session中的所有task,为它们重新设置一遍代理;一般来说初始化的session中的task应该是为空的,这里这么做的主要目的是为了防止从后台回来的时候初始化session,对于一些后台之前的请求任务没有重设代理导致崩溃的问题;这里里边不同的任务调用不同的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];
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

这个方法主要是把代理和task建立映射关系,并且存储到事先声明好的字典当中;同时为当前task添加监听;在添加监听的方法中,taskDidResume和taskDidSuspend为接收到通知后的处理方法;用来恢复任务和暂停任务;至此,对于初始化的时候获取当前session中的所有task,已经为它们重新设置一遍代理。回到initWitchSessionConfiguration方法中返回当前对象,向上返回,生成AFHTTPSessionManager *sessionManger对象;

一般我们运用网络请求的方法主要有GET和POST两种,以GET为例以下是图解流程,然后对比源码层面进行分析

ios Foundation 源码 ios 开发源码_ios Foundation 源码_05

GET源码内部做了以下工作:

/*URLString URL值
parameters 根据客户端请求序列化程序编码的参数
headers 附加到此请求头的头部标识
uploadProgress 更新要上传进度时要执行的块对象
downloadProgress 更新要下载进度时要执行的块对象。注意这个块是在会话队列上调用的,而不是在主队列上
success 当任务成功完成时要执行的块对象。该块没有返回值,并接受两个参数:data参数和客户端响应序列化创建的响应对象
failure 当任务未成功完成或成功完成时执行的块对象,但在解析响应数据时遇到错误。该块没有返回值,并接受两个参数:数据任务和描述发生的网络或解析错误的错误
 */
- (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
{
    //调用dataTaskWithHTTPMethod方法生成一个datatask任务
    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                          headers:headers
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];
    
    [dataTask resume];
    
    return dataTask;
}

GET方法内部一是生成datatask,二是开启请求;

生成datatask进入到AFHTTPSessionManager中的dataTaskWithHTTPMethod方法,进去看看:

- (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;
    //设置request相关属性&参数。(requestSerializer:请求序列化器)
    //1.⚠️使用请求序列化类中的requestWithMethod方法进行序列化处理。
    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) {
        	//completionQueue如果设置了这个GCD的queue,那么从这个completionQueue回调就好;否则就从主队列回调
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
        }

        return nil;
    }
	//NSURLSessionManger创建一个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;
}

解决一下两个问题:

1.调用请求序列化类中的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]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }
	//对mutableRequest参数做编码并且重新赋值给mutableRequest
	//⚠️调用requestBySerializingRequest方法将传入的parameters参数进行编码,并添加到request
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

	return mutableRequest;
}

总结一下:
在requestWitchMethod方法中,做了三件事情:

  1. 创建mutableRequest并设置其请求方法;
  2. 把当前类设置的一些属性设置给mutableRequest;(存在疑问)
  3. 把需要传递的参数进行编码并且设置到mutableRequest当中;

看看编码的方法:

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(request);

    NSMutableURLRequest *mutableRequest = [request mutableCopy];

    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];

    NSString *query = nil;
    if (parameters) {
        if (self.queryStringSerialization) {
            NSError *serializationError;
            query = self.queryStringSerialization(request, parameters, &serializationError);

            if (serializationError) {
                if (error) {
                    *error = serializationError;
                }

                return nil;
            }
        } else {
            switch (self.queryStringSerializationStyle) {
                case AFHTTPRequestQueryStringDefaultStyle:
                    query = AFQueryStringFromParameters(parameters);
                    break;
            }
        }
    }

    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        if (query && query.length > 0) {
            mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
        }
    } else {
        // #2864: an empty string is a valid x-www-form-urlencoded payload
        if (!query) {
            query = @"";
        }
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
        }
        [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
    }

    return mutableRequest;
}
  1. 从当前请求队列中拿到self.HTTPRequestHeaders中拿到设置的参数,赋值要请求的request中;(疑问点)
  2. 把网络请求的参数转换为NSString类型,这一步是对参数进行转码;
  3. 将请求方法拼接到url中;GET、HEAD、DELETE这几个method的quey是拼接到url后面的。而POST、PUT是把query拼接到http body中的;

以下这几个方法就是我们在转码的时候所使用的方法,如何将请求参数进行转码:也就是通过递归调用并解析,直到解析的除了array,dic,set以外的元素,然后将最终得到的参数返回;

ios Foundation 源码 ios 开发源码_序列化_06

到这里序列化的 部分结束,返回一个NSMutableURLRequest实例;紧接着设置请求头并且通过回调处理序列化失败的情况;

那么了解以后,我们回答一下为什么要进行序列化?
在HTTp网络请求是基于字节流的网络传输,序列化是可以将一个对象转化成一段字节编码,以此方便在网络上传输或者做其他存储处理,使用的时候在对其做反序列化;简单的来说就是为了统一,我们可以用自己的方法来保存对象,但是在底层只提供一种保存对象状态的机制。因此我们在存储的时候需要进行序列化,以此来和提供的保持一致,在读取的时候对其进行反序列化,得到我们想要的形式;

2.调用dataTaskWithRequest来生成一个datatask任务
在调用的时候,将序列化后的request,等参数传入,先看看dataTaskWithRequest方法里边做了什么事情吧:

- (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;
}

可以看到,生成一个datatask,并且调用addDelegateForXXX方法来设置代理,那么,设置代理这一块,和在初始化的时候为当前初始化session中的task设置代理的过程是一样的。(向上👆查看初始设置代理部分);

最终返回一个dataTask任务,创建完成之后通过回调返回进度以及comple情况,在dataTaskWithHTTPMethod方法中进行处理;这里回到dataTaskWithHTTPMethod方法中,做完上述处理之后,最终将生成的dataTask返回到GET方法当中,这样最终在GET中就拿到了我们可以发送请求的task,最后调用系统方法[dataTask resume**];发起网络请求;当然,在GET方法中有progress,success,failure回调,使用的时候直接根据回调来处理不同情况,不用像原生的方法那样,进行多次判断,确认哪一种情况,再去做处理;

回到我们的初始化方法,在调用AFURLSessionManager的initWithSessionConfiguration方法初始化一个sessionManager的时候,在进行懒加载的时候,初始化session:

ios Foundation 源码 ios 开发源码_ios Foundation 源码_07