我们回顾一下刚开始写的那段代码
AFHTTPSessionManager *client = [[AFHTTPSessionManager alloc] init];
[client GET:@"http://localhost" parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
初始化了一个AFHTTPSessionManager的实例,然后发送了一个请求,那我们就直接点进去看这个请求。
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
headers:headers
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
[dataTask resume];
return dataTask;
}
记得我们说过AFHTTPSessionManager只是是对父类AFSessionManager的封装,实际情况也是这样的这个方法直接调用父类的方法生成了一个dataTask然后启动了它。我们进到父类的方法里面读代码。
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
headers:(NSDictionary <NSString *, NSString *> *)headers
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;
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 addValue: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;
}
可以看到这个方法里面还是做了一些事情的
- 生成了NSMutableURLRequest的实例对象
- 判断request对象生成无问题后调用方法生成了dataTask,然后返回给调用方。
但是我们依然没看到由session生成task的代码,没关系,我们可以看到生成task的一个关键参数request,而且对于一个请求来说request也是非常中要的一部分,我们线看request的生成。
Request的生成
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 addValue:headers[headerField] forHTTPHeaderField:headerField];
}
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
我们依然从外面向里面读,先看看这段代码做了什么,然后再到requestSerializer里面看一看。
- 调用requestSerializer生成request的方法,同时把serializationError的指针传进去下面用来做异常处理
- 遍历参数headers,如果有值则为request添加相应的配置项
- 检查生成request的时候是否出现了异常,如果有异常则从completionQueue队列或者从主队列返回
这里面又出现了一个completionQueue,我们之前看到过一个operationQueue了,这里的completionQueue是做什么的,我们依然要搞清楚。
/**
The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used.
*/
@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;
注释我们可以看到它是完成回调数据的时候引用的队列,如果外部输入了这个队列则我们用这个来回调数据,如果没有,那我们从主队列返回数据。那这个队列在什么情况下使用呢,我在这里说一下我在项目中的应用,我们公司的网络库数据的编码不是基于json的,有一套自己的解码逻辑,所以我得到http返回的字节流之后,将字节流的解码放在了这个completionQueue,即在初始化的时候我会给这个completionQueue赋值,代码片段如下
_engineQueue = dispatch_queue_create("EngineQueue", DISPATCH_QUEUE_SERIAL);
_sessionManager = [[AFURLSessionManager alloc] initWithSessionConfiguration:self.configuration];
_sessionManager.responseSerializer = [AFHTTPResponseSerializer serializer];
_sessionManager.completionQueue = self.engineQueue;
这样我就减小了主队列的压力,该工作线程做的事情,例如数据操作交还给工作线程做,主队列还是应负责UI刷新相关的工作。
深吸一口气,我们来看这request生成的代码吧。
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
- 根据URLString生成了一个mutableRequest,然后给这个对象的method赋值
- 遍历AFHTTPRequestSerializerObservedKeyPaths,如果发现self.mutableObservedChangedKeyPaths中包含这个keyPath,则设置mutableRequest。
好像在说天书,好吧,我承认,我们还是把代码贴出来,分析一下原理,看看他到底在干什么吧。
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;
}
AFHTTPRequestSerializerObservedKeyPaths是一个c数组,里面含有一些字符串。(allowsCellularAccess、cachePolicy、HTTPShouldHandleCookies、HTTPShouldUsePipelining、networkServiceType、timeoutInterval),这些东西在哪里呢看一下NSURLRequest.h文件就找到了,希望大家辛苦自己查阅一下吧。
然后是这个东西:self.mutableObservedChangedKeyPaths。
self.mutableObservedChangedKeyPaths = [NSMutableSet set];
//给这自己些方法添加观察者为自己,就是request的各种属性,set方法
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
}
}
这个集合在初始化代码中创建,然后对当前类的于 NSURLRequest相关的属性进行了KVO监听
看一下KVO:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(__unused id)object
change:(NSDictionary *)change
context:(void *)context
{
if (context == AFHTTPRequestSerializerObserverContext) {
if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
[self.mutableObservedChangedKeyPaths removeObject:keyPath];
} else {
[self.mutableObservedChangedKeyPaths addObject:keyPath];
}
}
}
至此我们知道self.mutableObservedChangedKeyPaths其实就是我们自己设置的request属性值的集合。
然后调用这个方法,用KVC的方式,把属性值都设置到我们请求的request中去。
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
- 最后是调用序列化方法将传入的参数进行序列化,放到request中去
//将传入的parameters进行编码,然后放到request中去
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
//遍历自己的header,如果有值的话设置给request的header
[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 {
// AFNetworking自定义的序列化方式
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
//如果request的method为get,header,delete,则将query参数拼到url后面,否则放到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"];
}
//设置body参数
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}
- 看一下自己的header,如果有值,则设置这个request的header
- 序列化parameters,如果用户自定定了序列化方式,按照用户的来,如果没有,我们使用afn默认的。其实大部分情况下我们都是使用默认的。
我们来看一下这个默认的方式
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
NSMutableArray *mutablePairs = [NSMutableArray array];
//给AFQueryStringPairsFromDictionary传进去parameters,得到AFQueryStringPair的对象
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
//AFQueryStringPair对象进行编码,然后放到mutablePairs这个数组中
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
//数组中的数据取出来,然后用"&"拼接成字符串
return [mutablePairs componentsJoinedByString:@"&"];
}
NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
//根据对象的description进行排序,selector使用的是compare,对象的description是字符串类型的,即这里用的字符串生序排序
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
// 根据传入的value类型进行递归调用,知道value非集合类型的数据时,将得到的参数数组返回
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 ]]) {
id nestedValue = dictionary[nestedKey];
if (nestedValue) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
}
}
} else if ([value isKindOfClass:[NSArray class]]) {
NSArray *array = value;
for (id nestedValue in array) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
}
} else if ([value isKindOfClass:[NSSet class]]) {
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;
}
// AFQueryStringPair 类型数据编码
- (NSString *)URLEncodedStringValue {
if (!self.value || [self.value isEqual:[NSNull null]]) {
return AFPercentEscapedStringFromString([self.field description]);
} else {
return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];
}
}
我们列举一个例子来直观的看一下这部分代码做了什么
@{
@"name" : @"bang",
@"phone": @{@"mobile": @"xx", @"home": @"xx"},
@"families": @[@"father", @"mother"],
@"nums": [NSSet setWithObjects:@"1", @"2", nil]
}
->
@[
field: @"name", value: @"bang",
field: @"phone[mobile]", value: @"xx",
field: @"phone[home]", value: @"xx",
field: @"families[]", value: @"father",
field: @"families[]", value: @"mother",
field: @"nums", value: @"1",
field: @"nums", value: @"2",
]
->
name=bang&phone[mobile]=xx&phone[home]=xx&families[]=father&families[]=mother&nums=1&num=2
至此我们就清楚了,传入request的patameters将会放到url的query参数的地方。跟我们平时在浏览器里看到的是一样的。
- 说完query我们来看一下body参数,如果request的方法非get,header,delete,则设置body参数
//如果request的method为get,header,delete,则将query参数拼到url后面,否则放到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"];
}
//设置body参数
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
代码里面有一句给header的Content-Type 赋值的操作[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
我们顺路看一下post方式的这些Content-Type。
这四种常见的POST数据传输的方式大家可以点进去看一下。
到这里,我们已经生成了一个request,后面的任务就是如何将这个request丢出去,然后等待数据返回了。