AFNetworking源码解析
很久之前就打算解读AFN的源码,也尝试看了几遍,但是可能是由于自己功力还不够,总是感觉看起来很吃力,觉得在云里雾里。最近又重新开始尝试阅读AFN的源码,发现好了很多。接下来就把我所理解的记录下来,一是加深印象,二是时间久了可以拿出来翻阅一下。
AFNetworking
是一个用来做网络请求的第三方库,它是对NSURLSession
做网络请求的一个封装。下面我们先从一个简单的GET
请求开始,从整体上把握一下AFNetworking
是如何做网络请求的。
整体阅读
在整体阅读源码之前我们先看一下使用NSURLSession
做网络请求的步骤:
//创建Configuration对象,并设置各种属性
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.timeoutIntervalForRequest = 10.0;
configuration.allowsCellularAccess = YES;
//通过Configuration创建session,一个session可以管理多个task
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186.32812/login"];
//通过URL创建request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//设置request的请求方法和请求体
request.HTTPMethod = @"POST";
request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
//通过session和request来创建task
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
}];
[dataTask resume];
总结一下,使用NSURLSession
进行网络请求主要分为以下几个步骤:
- 1.创建
configuration
对象,这个这个对象可以设置进行网络请求的一些属性,例如timeoutIntervalForRequest
,allowsCellularAccess
等等。 - 2.通过
configuration
对象创建session对象,一个session
对象可以管理多个task。 - 3.通过URL创建request对象,通俗的理解,一个网络请求包括起始行,请求头,请求体,那么一个request对象也就封装了起始行,请求头,请求体这些内容。另外,其实request也可以行使configuration的功能,通过它的属性可以设置
timeoutIntervalForRequest
,allowsCellularAccess
等等,这个后面一点我们会看到。 - 4.通过
session
和request
来创建task对象。
下面我们使用AFNetworking来做一个网络请求:
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSString *url = @"http://120.25.226.186.32812";
[manager GET:url parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
这里我们也可以看到,使用AFNetworking
进行一个网络请求非常的简单,点进去:
- (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
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
[dataTask resume];
return dataTask;
}
这个很简单,创建了一个dataTask
然后启动了这个task。接下来看一下dataTask
是如何创建的:
- (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;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
__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;
}
这里分为了两步,第一步是创建了一个NSMutableRequest
对象,第二步是使用这个NSMutableRequest
对象创建了NSURLSessionDataTask
对象,到这里是不是就有点像用NSURLSession
做网络请求呢?
我们先看第一步:
- (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;
}
我们看一下下面用KVC给mutableRequest
的属性设值的部分:
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
看一下AFHTTPRequestSerializerObservedKeyPaths()
这个方法:
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;
}
这个方法很明显,就是把 NSMutableURLRequest
的一些属性转化成字符串数组返回。
然后如果self.mutableObservedChangedKeyPaths
包括这个数组中的某个属性,那么就给mutableRequest
对象的这个属性通过KVC设值。到这里你一定很好奇这个mutableObservedChangedKeyPaths
到底是个什么东西。
我们尝试在文章开头使用AFNetworking
进行网络请求那个Demo里加上下面这句:
manager.requestSerializer.allowsCellularAccess = YES;
然后在- (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(id)parameters error:(NSError *__autoreleasing *)error
这个方法里打断点,打印self.mutableObservedChangedKeyPaths
的值,打印出来的结果应该是
那么这就很明显了,也就是我们在外面通过
requestSerializer
设置的一些网络请求的属性,在内部转化到了self.mutableObservedChangedKeyPaths
这个数组内部,再通过比对给request
的这些属性设值。接下来我们看一下在外部给requestSerializer
的一些属性设值是怎么转化到self.mutableObservedChangedKeyPaths
这个数组内部的:
- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess {
[self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
_allowsCellularAccess = allowsCellularAccess;
[self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
}
当我们设置manager.requestSerializer.allowsCellularAccess = YES;
时代码会执行其set方法,在set方法里面,很明显是手动触发了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];
}
}
}
这样就把这个key加到了self.mutableObservedChangedKeyPaths
数组里。
上面创建mutableRequest
的第一步完成了,我在文章开头讲过,一个NSMutableRequest
对象封装了请求的起始行,请求头,请求体,除此之外还可以设置请求的一些属性,比如allowsCellularAccess
等等。那么第一步也就完成了起始行的配置,以及请求的一些属性的设置,还剩下请求头和请求体的配置,那么第二步就是为了完成这个工作:
mutableRequest = [[self requestBySerializingRequest:
mutableRequest withParameters:parameters error:error] mutableCopy];
创建mutableRequest第二步
这里我们先看第一步:为
mutableRequest
设置请求头
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
self.HTTPRequestHeaders
是在AFHTTPRequestSerializer
的初始化方法里面创建的,self.HTTPRequestHeaders
主要包含两个请求头。即Accept-Language
和User-Agent
。这里就是为mutableRequest
设置了这两个请求头。
继续看第二步:将参数进行转化,并用=,&连起来
这一步的核心就是query = AFQueryStringFromParameters(parameters);
这一句,我们看一下AFQueryStringFromParameters()
方法的实现:
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
NSMutableArray *mutablePairs = [NSMutableArray array];
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
return [mutablePairs componentsJoinedByString:@"&"];
}
这里的策略就是通过AFQueryStringPairsFromDictionary()
这个方法把parameters
转化为一个个AFQueryStringPair
对象,然后把这些对象都加入mutablePairs
这个数组中,最后使用&
把数组中的对象连起来,组成一个字符串。
先了解一下AFQueryStringPair
这个类,这个类有两个属性,一个是field
,一个是value
,还有一个方法- (NSString *)URLEncodedStringValue
,这个方法就是把field
和value
这两个属性用=
连起来,组成字符串。
- (instancetype)initWithField:(id)field value:(id)value {
self = [super init];
if (!self) {
return nil;
}
self.field = field;
self.value = value;
return self;
}
- (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])];
}
}
接下来再来看一下AFQueryStringPairsFromDictionary()
这个方法处理parameters
的策略,这个策略要说起来比较麻烦,这里我就用三个例子来解释一下具体策略:
- 1.当
parameters
是NSDictionary
类型时
id parameters = @{@"name" : @"pdd",
@"sex" : @"man",
};
返回的字符串:
name=pdd&sex=man
- 2.当parameters是NSDictionary且
NSDictionary
中含有NSDictionary
时
id parameters = @{@"name" : @"pdd",
@"sex" : @"man",
@"friends" : @{@"name" : @"xiaoming", @"sex" : @"man"}
};
返回的字符串:
name=pdd&sex=man&friends[name]=xiaoming&friends[sex]=man
- 3.当
parameters
是NSArray
类型时
id parameters2 = @[@"pdd", @"man"];
返回的字符串:
[]=pdd&[]=man
第二步结束了,我们再返回去,看第三步,如果方法是GET
,HEAD
,DELETE
,则直接将第二步得到的字符串粘贴在请求URL后面。
第四步,当方法不是上面的方法时,需要为mutableRequest
设置Content-Type
这个请求头,并且将上面第二步得到的参数字符串作为mutableRequest
的请求体。
这里为什么对于
GET
,HEAD
,DELETE
这三个方法不需要设置Content-Type
这个请求头呢?这是因为根据RFC 7231的规定,只有PUT
和POST
请求的时候,Content-Type
才是必须的。
到这里request
的创建和设置就完成了,下面继续看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 {
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
url_session_manager_create_task_safely
主要是解决了iOS8之前的一个系统Bug,我简单解释一下这个Bug:
在每一个NSURLSession中,每个NSURLSessionDataTask的taskIdentifier应该是独一无二的,不能和其他的NSURLSessionDataTask重叠。但是在iOS8之前,当我们并行的创建一些NSURLSessionDataTask时,这些NSURLSessionDataTask的taskIdentifier有可能重叠。
解决办法:在iOS8之前使用同步串行队列来实现他们的同步有序创建,在iOS8之后,这个Bug已经被修复。
我们看到,url_session_manager_create_task_safely
正是创建了一个同步串行队列:
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
//url_session_manager_creation_queue()是一个串行队列
dispatch_sync(url_session_manager_creation_queue(), block);
} else {
block();
}
}
接下来看一下:
[self addDelegateForDataTask:dataTask
uploadProgress:uploadProgressBlock
downloadProgress:downloadProgressBlock
completionHandler:completionHandler];
这个方法非常重要
- (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
{
//NSURLSessionDataTask的代理方法在AFURLSessionManager.m中实现,然后在AFURLSessionManager.m中调用AFURLSessionManagerTaskDelegate的方法
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
我们先来梳理一下AFURLSessionManager
,AFURLSessionManagerTaskDelegate
,NSURLSessionDataTask
这三个类之间的关系:
上图中,在
实现协议方法 这里也确实实现了这些代理方法,但是有一些代理方法其实是在代理方法内部交给了AFURLSessionManager
这个类中,创建了session对象,并指定自己为session的delegate,这意味着AFURLSessionManager
这个类需要实现NSURLSessionDelegate
,NSURLSessionTaskDelegate
,NSURLSessionDataDelegate
,NSURLSessionDownloadDelegate
这些协议的方法。AFURLSessionManagerTaskDelegate
这个类去实现,比如下面的代理方法: 交给AFURLSessionManagerTaskDelegate.png 所以总结一下就是,AFURLSessionManager
需要将NSURLSessionDataTaask
和AFURLSessionManagerTaskDelegate
对应起来,这样在NSURLSessionDataTaask
的代理方法中就可以获取到task对应的AFURLSessionManagerTaskDelegate
然后交给它去处理。
再回到代码中
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
//KVO,为task添加观察者,观察者为AFURLSessionManagerTaskDelegate
[delegate setupProgressForTask:task];
//添加通知
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
- (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
{
//NSURLSessionDataTask的代理方法在AFURLSessionManager.m中实现,然后在AFURLSessionManager.m中调用AFURLSessionManagerTaskDelegate的方法
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
[delegate setupProgressForTask:task];
即为task的一些下载和上传的属性使用KVO添加观察者:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
}
}
else if ([object isEqual:self.downloadProgress]) {
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}
else if ([object isEqual:self.uploadProgress]) {
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object);
}
}
}
总结一下AFURLSessionManagerTaskDelegate
这个类的作用:
- 1.分担一部分
AFURLSessionManager
的工作,在一些AFURLSessionManager
实现的代理方法中,直接调用了AFURLSessionManagerTaskDelegate
的相关方法。这个在之前讲过,不再赘述。 - 2.对于上传和下载,监听上传和下载的进度并产生回调,具体过程如下:
1> 2> 3> 9E530F9C-93C7-4003-88F4-8F89262FD102.png
到这里,一个完整的GET
请求就完成了。
AFHTTPRequestSerializer
AFHTTPRequestSerializer
这个类的作用是将request
序列化,通俗的讲,就是为request
设置起始行,请求头,请求体,以及一些其他的网络请求属性。
它有两个子类:
由于
AFHTTPRequestSerializer
遵循AFURLRequestSerialization
协议,所以其两个子类也遵循这个协议,这个协议只有一个方法:
- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(nullable id)parameters
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
默认情况下是使用AFHTTPRequestSerializer
,但是当我们为manager.requestSerializer
赋值时,可使用AFJSONRequestSerializer
或AFPropertyListRequestSerializer
:
manager.requestSerializer = [AFJSONRequestSerializer serializer];
AFHTTPRequestSerializer
,AFJSONRequestSerializer
,AFPropertyListRequestSerializer
三个类的不同体现在对协议方法的实现上:
AFHTTPRequestSerializer:
- (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) {
//queryStringSerialization是一个Block
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;
}
}
}
//GET, HEAD, DELETE
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
if (query && query.length > 0) {
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
}
//如果不是以上三种方法,需要设置Content-Type首部,并把query查询语句加入body中
} 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;
}
AFJSONRequestSerializer:
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
return [super requestBySerializingRequest:request withParameters:parameters error:error];
}
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
if (parameters) {
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];
}
return mutableRequest;
}
AFPropertyListRequestSerializer:
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
return [super requestBySerializingRequest:request withParameters:parameters error:error];
}
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
if (parameters) {
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]];
}
return mutableRequest;
}
可以看到,当请求的方法是
GET
,HEAD
,DELETE
时,是直接调用了父类的方法的。当请求的方法不是这三个时,才会自行处理,自行处理的第一部分是为mutableRequest
设置公共的请求头,第二部分是处理参数,我们以AFURLRequestSerialization
为例:
if (parameters) {
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];
}
首先是设置Content-Type
,然后使用NSJSONSerialization
对参数序列化,然后设置为request
的请求体。
Type | Content-Type | data format |
---|---|---|
HTTP | application/x-www-form-urlencoded | UTF-8 data |
JSON | application/json | json data |
PropertyList | application/x-plist | p-list |
AFHTTPResponseSerializer
AFHTTPResponseSerializer
是响应序列化的工具类,它和AFHTTPRequestSerializer
很相似,有6个子类,并且遵循AFURLResponseSerialization
这个协议。
AFURLResponseSerialization
协议也只有一个协议方法:
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
在之前的源码中,我们好像从来没有用到AFHTTPResponseSerializer
及其子类,那么它是使用在哪里呢?AFHTTPResponseSerializer
及其子类是用在解析从网络端获取的response
。我们在AFURLSessionManager
的NSURLSessionTaskDelegate
这个代理中查看- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
这个方法:
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// delegate may be nil when completing a task in the background
if (delegate) {
[delegate URLSession:session task:task didCompleteWithError:error];
[self removeDelegateForTask:task];
}
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
接着查看AFURLSessionManagerTaskDelegate
这个类的同样的方法:
这个方法比较复杂,我们找到关键的一句:
responseObject = [manager.responseSerializer
responseObjectForResponse:task.response
data:data error:&serializationError];
然后我们查看这个方法的实现:
我们以AFJSONResponseSerializer
为例:
这里我们看一下判断
response
的有效性的步骤:- (BOOL)validateResponse:(NSHTTPURLResponse *)response data:(NSData *)data error:(NSError * __autoreleasing *)error
这个方法的实现在AFJSONResponseSerializer
的父类即AFHTTPResponseSerializer
中:
每一个
AFHTTPResponseSerializer
的子类在初始化时都设置了acceptableContentTypes
这个属性,比如AFJSONResponseSerializer
是这样初始化的:
self.acceptableContentTypes =
[NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
acceptableStatusCodes
是在AFHTTPResponseSerializer
类中初始化的:
self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
即范围为200-299。
上面讲到AFHTTPResponseSerializer
有6个子类,他们之间的区别其实就是根据需要把网络请求返回的数据解析成不同类型的数据,区别如下:
Class | Accept | Serializer |
---|---|---|
AFJSONResponseSerializer | application/json,text/json,text/javascript | NSJSONSerialization |
AFXMLParserResponseSerializer | application/xml,text/xml | NSXMLParser |
AFXMLDocumentResponseSerializer | application/xml,text/xml | NSXMLDocument |
AFPropertyListResponseSerializer | application/x-plist | NSPropertyListSerialization |
AFImageResponseSerializer | image/tiff,image/jpeg,image/gif,image/png,image/ico,image/x-icon,image/bmp,image/x-bmp,image/x-xbitmap,image/x-win-bitmap | NSBitmapImageRep |
AFCompoundResponseSerializer | 多种类型的集合 | - |
AFNetworkReachabilityManager
AFNetworkReachabilityManager
这个类和前面的网络请求类没有直接的关系,我们可以看到,在前面的解读中,也从来没有提到过AFNetworkReachabilityManager
这个类。这个类是用来监测设备的网络状态变化的,其使用如下:
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusUnknown:
NSLog(@"未知网络");
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"无网络连接");
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"蜂窝网络");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"WiFi网络");
break;
default:
break;
}
}];
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
当检测到设备的网络状况改变时会调用block。
- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block {
self.networkReachabilityStatusBlock = block;
}
上面的方法很简单,就是给自己的block属性赋值。
我们再看startMonitoring
这个方法:
AFHTTPSessionManager和AFURLSessionManager
AFHTTPSessionManager
是AFURLSessionManager
的子类,我们在使用的时候大多数时候其实是使用的AFHTTPSessionManager
,它更像是AFURLSessionManager
对外的一个接口,这个类提供的API很简单:
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
以上这些方法都会统一调用AFHTTPSessionManager.m
中的一个内部方法:
- (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;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
if (serializationError) {
if (failure) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
#pragma clang diagnostic pop
}
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;
}
在上面的内部方法中,requestSerializer
是父类的属性,后面创建dataTask的方法也是调用的父类的方法。
观察上面的API也可以发现,
AFHTTPSessionManager
中的API都是比较简单的请求,当我们涉及到文件的上传和下载等,还是要直接使用AFURLSessionManager
中的API:
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromFile:(NSURL *)fileURL
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
总结
总结AFHTTPSessionManager是一个管理类,在这个类的头文件中提供了进行网络请求的API。在这个类中有几个关键的属性,其作用如下:
- requestSerializer
这是进行请求序列化的属性,它的默认类是AFHTTPRequestSerializer
。这个属性的主要作用,就是为request
设置请求的起始行,请求头,请求体,还有其他例如请求超时时间等设置。如果我们想要把请求的parameters
封装为特殊的格式如Json或plist,那么我们可以使用AFHTTPRequestSerializer
的子类AFJSONSerializer
和AFPropertyListRequestSerializer
。 - responseSerializer
这个类是进行响应序列化的属性,默认是AFJSONResponseSerializer
。AFJSONResponseSerializer
主要做了两个工作,第一个是验证response
的有效性,验证有效性从两方面去验证,一方面是response
的MIME类型是否在已设置的acceptableContentTypes
,另一方面是验证response
的statusCode是否在200-299范围内,两个方面有一个方面不符合则视为无效的response
,不用解析,直接报错;第二个工作是对数据进行解析,从网络端得到的数据都是NSData类型的,需要对它进行转化,转化为我们需要的类型,如json,plist等。 - session
在我们创建一个dataTask的时候,不仅需要request
,还需要session
对象,在AFURLSessionManager
中,创建了session
对象来创建dataTask。 - reachabilityManager
这个属性创建出来是为了监听设备的网络状态使用的。 - securityPolicy
在网络请求的过程中遇到认证挑战时使用这个属性。 - AFURLSsssionManagerTaskDelegate
每一个dataTask都对应了一个AFURLSsssionManagerTaskDelegate对象,这种一一对应的关系存储在AFURLSessionManager
中,在AFURLSsssionManagerTaskDelegate
中实现了NSURLSessionDelegate
,NSURLSessionTaskDelegate
等协议,这其中有些协议的实现交给了AFURLSsssionManagerTaskDelegate
去成成。并且AFURLSsssionManagerTaskDelegate
还监听了dataTask的上传下载进度。
使用
AFNetworking
进行网络请求的大概过程为:
1.首先创建一个mutableRequest
,设置其请求方法,然后根据用户的设置,设置mutableRequest
的allowsCellularAccess
,timeoutInterval
等属性;然后设置mutableRequest
的公共请求头,主要是两个,User-Agent
和Accept-Language
;再把请求的parameters
转化为键值对,用=,&连起来组成字符串,如果请求方法是GET
,HEAD
,DELETE
这三个方法之一,则直接把parameters
字符串粘贴在请求URL后面,否则就要把parameters
字符串设置成请求的请求体,并设置Content-Type
请求头;到这里request
相关都配置好了,并且这些配置都是在requestSerializer
相关类中完成的。
2.创建dataTask,首先创建一个session对象,根据session对象和之前创建的request
对象来创建一个dataTask,然后创建一个AFURLSessionManagerTaskDelegate
的对象,把这个对象和dataTask关联起来,让AFURLSessionManagerTaskDelegate
对象通过KVO监听dataTask的上传下载进度,并执行相应的回调。
3.通过resume
启动dataTask。
4.在请求完成的代理方法中,会使用responseSerializer
来处理response
,首先检查response
的有效性,然后将其转化为相应的类型。