AFNetworking源码学习

简介:

AFNetworking是iOS、macOS、watchOS和tvOS的一个令人愉快的网络库。它建立在基础URL加载系统之上,扩展了构建到Cocoa中的强大的网络高级抽象。它有一个模块化的体系结构,具有设计良好、功能丰富的API,使用起来很愉快。

然而,也许最重要的特点是,每天都在使用AFNetworking并为其做出贡献的开发人员组成了一个令人惊叹的社区。AFNetworking为iPhone、iPad和Mac上一些最受欢迎、广受好评的应用提供了动力。

参考的大佬博客:

AFNetworking详解
AFNetworking介绍
AFNetWorking源码学习(一)——简述

内容:

目前自身常用的网络请求方法大多都是原生的网络请求,一直都仅限于会用,但是对于整个请求的流程却不清楚,所以借分析AFNetworking的源码去一探究竟。

我们疑惑的问题基本如下:

  1. 原生的方式是如何发送一个网络请求的?
  2. AFNetworking中都有哪些模块,作用是什么?
  3. 每个模块类之间的关系是什么?
  4. 利用 AFNetworking是如何来发送一个网络请求的?

首先我们看一下原生的网络请求发送方式:

这里只针对NSURLSession,不去研究在这之前的NSURLConnection,分别以GET和POST两种请求方式来进行使用:

1、GET请求方式
步骤:

  • 确定请求的路径URL
  • 创建可变的请求对象request:可省略((默认包含了请求头和请求方法【Get】),此步骤可以省略)
  • 创建会话session对象
  • 根据会话对象创建请求任务datatask,(利用dataTaskWithRequest或者dataTaskWithURL方法来进行创建)
  • 执行Task
  • 当得到服务器返回的响应后,解析数据(XML、JSON、HTTP)如果返回的数据是JSON格式的,因此使用NSJSONSerialization进行反序列化处理
	//GET网络请求
    NSURL *getUrl = [NSURL URLWithString:@"https://img1.baidu.com/it/u=1966616150,2146512490&fm=253&fmt=auto&app=138&f=JPEG?w=751&h=500"];
    NSURLSession *getSession = [NSURLSession sharedSession];
    //方法一
    NSMutableURLRequest *getRequest = [NSMutableURLRequest requestWithURL:getUrl];
    getRequest.HTTPMethod = @"GET";
    NSURLSessionDataTask *getDatatask = [getSession dataTaskWithRequest:getRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"%@", data);
        NSLog(@"%@", response);
    }];
    //方法二
    NSURLSessionDataTask *getDatatask = [getSession dataTaskWithURL:getUrl completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"%@", data);
        NSLog(@"%@", response);
    }];
    [getDatatask resume];

2、POST请求
步骤:

  • 确定请求的路径URL
  • 创建可变的请求对象request:不可省略——POST请求需要设置请求头
  • 设置请求头(需要对创建好的请求头进行序列化:确保为服务端可识别的数据格式)和请求方式POST
  • 创建会话session对象
  • 根据会话对象创建请求任务datatask——(通过dataTaskWithRequest)
  • 执行Task
  • 当得到服务器返回的响应后,解析数据(XML、JSON、HTTP)如果返回的数据是JSON格式的,因此使用NSJSONSerialization进行反序列化处理
	//POST网络请求
	NSString *stringPhoneApi = @"http://116.62.180.44:8081/community";
    NSURL *urlString = [NSURL URLWithString:stringPhoneApi];
    NSMutableURLRequest *requestTest = [NSMutableURLRequest requestWithURL:urlString];

    [requestTest setHTTPMethod:@"POST"];
    
    //设置网络请求的请求体(请求体为json类型的字符串)
    //下方为将普通字符串转换为json类型的字符串
    NSDictionary *dict = @{@"author":upData.author, @"content":upData.content, @"time":upData.time, @"avatar":upData.avatar, @"picture":upData.picture, @"reContent":upData.reContent, @"reAuthor":upData.reAuthor, @"rePicture":upData.rePicture};
    
    NSData *dictPhoneData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil];
    NSLog(@"%@", [NSJSONSerialization JSONObjectWithData:dictPhoneData options:0 error:nil]);
    
    requestTest.HTTPBody = dictPhoneData;
    
    //设置请求头
    [requestTest addValue:@"application/json;UTF-8" forHTTPHeaderField:@"Content-Type"];
    
    NSURLSession *sessionTest = [NSURLSession sharedSession];
    
    NSURLSessionDataTask *testDataTask = [sessionTest dataTaskWithRequest:requestTest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error == nil) {
        	//NSLog(@"网络请求成功!");
            succeedBlock();
        } else {
            //NSLog(@"网络请求失败!");
            errorBlock(error);
        }
    }];
    
    [testDataTask resume];

从上方我们可以看到,原生的网络请求也可以实现我们所需要的功能,但是为什么要有AFNetworking的存在呢,这里就需要对比一下两者了,下方的分析会给出答案。

接着我们来看AFNetworking的实现:

1. 代码结构
请添加图片描述
目前所使用的是AFNetworking4.0的版本,基本结构与之前没有变化,类与类之间的关系如下图:
在这里插入图片描述
其中AFURLSessionManager是核心,其他的那四个黄色框的内容都是他的助手,也就是在AFURLSessionManager需要的时候提供帮助,最后实现全部的功能,(也就是说生成对应的sessionmanger,并且初始化并管理AFSecurityPolicy、AFNetworkReachabilityManager以及AFJSONResponseSerializer来一同为它所生成的sessionmanger服务)。

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

前方出场的是父子,而后方给予支援的就是我们的网络监控模块,安全策略模块以及序列化工具模块等;

2. 模块划分

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

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

3. 使用流程

我们以其中的GET方法为例,看一下它的使用步骤:

  • 生成对应的sessionManager;
  • 调用GET方法进行请求;
  • 在GET回调里边处理结果,包括(任务进度,请求成功的结果,请求失败的结果);
AFHTTPSessionManager *sessionManager = 
// parameters 参数字典 
[AFHTTPSessionManager manager];
    [sessionManager GET:@"https://img1.baidu.com/it/u=1966616150,2146512490&fm=253&fmt=auto&app=138&f=JPEG?w=751&h=500" parameters:nil headers:nil progress:^(NSProgress * _Nonnull downloadProgress) {
        NSLog(@"获取过程中的处理");
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"获取成功的处理");
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"获取失败的处理");
    }];

这短短的几行代码,就实现了我们需要的GET网络请求,接下来我们进入源码,看看AFNetworking内部为我们做了些什么,以下是整个流程图解;

下图是对AFHTTPSessionManager *sessionManager =[AFHTTPSessionManager manager]; 这行代码的详解,根据图解我们一起看源码:
在这里插入图片描述
首先是通过调用AFHTTPSessionManager的manager方发来进行初始化,那么来看一下manager方法:

@interface AFHTTPSessionManager ()
@property (readwrite, nonatomic, strong) NSURL *baseURL;
@end

@implementation AFHTTPSessionManager
@dynamic responseSerializer;

//instancetype是实例类型的意思
//这个方法就是初始化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
{
	//这里很关键,调用了父类的同名方法
    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:按预期工作, 若没有/时
    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;
   
    //吹初始化操作队列,并设置为串行队列 设置最大并发操作数
    self.operationQueue = [[NSOperationQueue alloc] init];
    //maxConcurrentOperationCount是最大并发操作数的意思
    self.operationQueue.maxConcurrentOperationCount = 1;
    
    //AFJSONResponseSerializer用来序列化HTTP响应
    self.responseSerializer = [AFJSONResponseSerializer serializer];
    //初始化SSL所需要的AFSecurityPolicy 用来保证请求的安全性
    self.securityPolicy = [AFSecurityPolicy defaultPolicy];

#if !TARGET_OS_WATCH
    //reachabilityManager是可达性管理器的意思, 用来判断网络的连接情况
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
    //mutableTaskDelegatesKeyedByTaskIdentifier是可变任务委托者的意思 (初始化可变任务字典,task的id作为key,代理对象作为value)
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    
    //初始化锁 & 重名
    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;

    //Completion Handler是完成处理程序的意思
    [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. 队列的最大并发操作数设置为1,这里的并发操作数值的是回调代理的线程并发数。
  2. self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];是用来将每一个请求任务和自定义的AFURLSessionManagerTaskDelegate来建立映射的;(需要深入研究,代理和这里的关系,以及利用KVO的思想实现的相关)
  3. 在初始化的时候获取当前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
{
    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任务;

//以key-value的形式存储task并且通过NSLock来保证不同线程的使用,避免出现线程竞争的问题
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    //NSLock来保证不同线程的使用,避免出现线程竞争的问题
    [self.lock lock];
    //向保存任务的可变字典中添加代理(task的id作为key,代理对象作为value)
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    //为仍无添加解析
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

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

(2)接下来,利用生成的sessionManager发起请求:来看看****sessionManager调用GET方法都做了哪些事,以下是图解流程,然后对比源码层面进行分析:
在这里插入图片描述

AFHTTPSessionManager *sessionManager = 
// parameters 参数字典 
[AFHTTPSessionManager manager];
    [sessionManager GET:@"https://img1.baidu.com/it/u=1966616150,2146512490&fm=253&fmt=auto&app=138&f=JPEG?w=751&h=500" parameters:nil headers:nil progress:^(NSProgress * _Nonnull downloadProgress) {
        NSLog(@"获取过程中的处理");
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"获取成功的处理");
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"获取失败的处理");
    }];

先看看GET方法内部做了什么:

/**
创建并运行带有“GET”请求的“NSURLSessionDataTask”。
@param URLString用于创建请求URL的URL字符串。
@param parameters根据客户端请求序列化程序编码的参数。
@param headers追加到此请求的默认头的头。
@param downloadProgress更新下载进度时要执行的块对象。注意:此块在会话队列上调用,而不是在主队列上调用。
@param success任务成功完成时要执行的块对象。此块没有返回值,并接受两个参数:数据任务和客户机响应序列化程序创建的响应对象。
@param failure任务未成功完成或成功完成但在分析响应数据时遇到错误时要执行的块对象。此块没有返回值,并接受两个参数:数据任务和描述网络的错误或发生的解析错误。
@请参阅-dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
*/
- (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];
    //调用resume,开始请求
    [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:请求序列化器)
    //调用请求序列化类中的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;
    }

    //NSURLSessionManager创建一个datatask
    //调用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值,存到创建的muatableRequest中
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        //mutableObservedChangedKeyPaths是可变观测变更路径的意思
        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方法中,做了三件事情:

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

看看编码的方法:

- (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) {
        //如果设置了构建query的块,就尝试调用块生成
        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来进行编码
                    query = AFQueryStringFromParameters(parameters);
                    break;
            }
        }
    }

    //判断该request中是否包含了GET、HEAD 、DELETE(都包含在HTTPMethodsEncodingParameetersInURI)
    //因为这几个method的quey是拼接到URL后面的,而POST、PUT是吧query拼接到http body中的
    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"];
        }
        //stringEncoding:用于序列化参数的字符串编码,默认“NSUTF8StringEncoding”
        [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
    }

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

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

其中的转码的函数AFQueryStringFromParameters(parameters); 的实现如下:

FOUNDATION_EXPORT NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary);
FOUNDATION_EXPORT NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value);

//下方的这三个函数依次调用,实现了对参数的转码
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
    NSMutableArray *mutablePairs = [NSMutableArray array];
    //把参数给AFQueryStringPairsFromDictioniary,拿到AF的一个类型的数据就一个key,value对象,在UELEncodedStringValue拼接key value,一个加到数组里
    for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
        [mutablePairs addObject:[pair URLEncodedStringValue]];
    }
    //拆分数组返回参数字符串,将数组传化为字符串
    return [mutablePairs componentsJoinedByString:@"&"];
}

NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
    return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}

//递归调用并解析key - value 直到解析的除了array,dic,set以外的元素,然后把最终得到的参数返回:
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
    NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];

    //根据需要排列的对象的description来进行升序排序,并且selector使用的是compare:
    //因为对象的description返回的是NSString,所以此处compare:使用的是NSString的compare函数
    //即@[@"foo", @"bar", @"bae"] ---> @[@"bae", @"bar", @"foo"]
    //sortDescriptor是排序描述符的意思
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];

    if ([value isKindOfClass:[NSDictionary class]]) {
        NSDictionary *dictionary = value;
        // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
        //对字典键进行排序以确保查询字符串中的顺序一致,这在反序列化潜在的不明确序列(例如字典数组)时非常重要
        for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
            //dic
            id nestedValue = dictionary[nestedKey];
            if (nestedValue) {
                //递归调用,第一次执行时key为nil,后续执行时key就有了实际的值,所以就可以按照刚才排好的序,去添加相应的键值对,且第一次传进来的value是容器形式,次for循环中的函数调用传的value是容器内部的元素形式,走的是最后的else,添加键值对
                [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
            }
        }
    } else if ([value isKindOfClass:[NSArray class]]) {
        //array
        NSArray *array = value;
        for (id nestedValue in array) {
            [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
        }
    } else if ([value isKindOfClass:[NSSet class]]) {
        //set
        NSSet *set = value;
        for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
            [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
        }
    } else {
        [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
    }

    return mutableQueryStringComponents;
}

我们很清晰地看到这三个函数从上往下一个调用一个,最终实现了请求参数的转码

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

为什么要进行序列化?

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

2、调用dataTaskWithRequest来生成一个datatask任务
在调用完requestWithMethod: 方法之后,我们接着调用了另一个很重要的方法dataTaskWithRequest 来生成一个datatask任务

在调用的时候,将序列化后的request,等参数传入,先看看dataTaskWithRequest方法里边做了什么事情吧:

//创建一个dataTask
- (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];
    //设置其delegate,包括进度、等
    [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回调,使用的时候直接根据回调来处理不同情况,不用像原生的方法那样,进行多次判断,确认哪一种情况,再去做处理;
回到我们的初始化方法,在调用AFURLSessionManagerinitWithSessionConfiguration方法初始化一个sessionManager的时候,在进行懒加载的时候,初始化session

- (NSURLSession *)session {
    
    @synchronized (self) {
        if (!_session) {
            _session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
        }
    }
    return _session;
}

由此看到,在初始化session的时候,将AFURLSessionManager作为了所有taskdelegate。因此当我们进行网络请求的时候,这些代理就会被执行。AFURLSessionManager遵守的代理包括:NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying;关于AFURLSessionManager以及他的代理我们后续再讲。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用AFNetworking库下载文件时,有几种常见的方式。第一种方式是使用AFHTTPRequestOperation进行文件下载,需要传入文件的下载地址URL、自定义的文件名fileName以及下载到的文件路径d_path。 另一种方式是使用AFHTTPSessionManager结合NSURLSessionDownloadTask进行文件下载。同样需要传入文件的下载地址URL和下载到的文件路径d_path。 此外,还可以使用AFNetworking库来实现文件的断点下载功能。通过对AFNetworking3.0版本的学习,你可以了解到如何使用AFNetworking库来实现文件断点下载,并且可以获取一些有价值的参考信息。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [AFNetworking从指定网址下载文件的两种方式总结](https://blog.csdn.net/u014063717/article/details/52232858)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [iOS利用AFNetworking3.0——实现文件断点下载](https://download.csdn.net/download/weixin_38607784/12786948)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值