开门见山,我们引入"AFNetworking.h"然后写个GET请求
image.png
然后从init方法进去,看看manager是什么东西
- (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;
}
// 确保url路径有效,使+URLWithString:relativeToURL:如期生效
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url;
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
作者在init方法里面储存了url路径,两个序列化标准
接下来点进父类initWithSessionConfiguration:
方法里面
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
//创建线程,并设置并发为1
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
//创建存储NSURL task与AFURLSessionManagerTaskDelegate的词典
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
//创建线程安全锁
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
//此处是为了防止从后台回来,之前的一些请求导致crash
[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;
}
此处AF设置的最大并发数为1,并创建一个key为task,value为AFURLSessionManagerTaskDelegate
的可变词典,至于为什么这么做,我们接下来看看网络请求部分
点进GET请求部分
- (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
{
//创建请求的dataTask
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
[dataTask resume];
return 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;
//把参数转化为一个request
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
//如果解析错误,直接返回failure
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;
}
这里主要做了两件事,一件是利用参数和requestSerializer
创建了一个NSMutableURLRequest
的实例,另一件就是利用dataTaskWithRequest:
创建了一个dataTask
至于这两个,我们先进去requestSerializer看看这个类究竟做了什么
进到AFURLRequestSerialization这个类中
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
//使用断言,如果没有这两个参数,程序将会crash
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
//遍历request各种属性
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
//如果自己观察到的属性包含在里面
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
//将自己的属性设置request里面
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
//将传入的参数进行编码
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
这个方法设置了请求方式method,除此之外,还获取了NSUrlRequest
的一些属性,其中 AFHTTPRequestSerializerObservedKeyPaths()
是一个c方法,创建一个单例数组,包含了以下属性
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;
}
还有mutableObservedChangedKeyPaths
是当前类的一个属性,在init方法里面设置了监听
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) {
if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
[self.mutableObservedChangedKeyPaths removeObject:keyPath];
} else {
[self.mutableObservedChangedKeyPaths addObject:keyPath];
}
}
}
方法实际上就是,如果设置的值为Null就删除这个key,反之则添加
到此我们知道这个mutableObservedChangedKeyPaths
就是存储request属性的集合,再用KVC的方式把值设置我们的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];
//将request的header放入自己的requestHeader
[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;
}
}
}
//最后判断该request中是否包含了GET、HEAD、DELETE(都包含在HTTPMethodsEncodingParametersInURI)。因为这几个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"];
}
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}
从上往下看,这个方法做了3个方面,
1.把系统的request的header赋值到自身的HTTPRequestHeaders
2.对参数进行解析
3.判断该request是否为GET,DELETE,HEAD这些方法,从而判断query添加的位置
重点看第2部分转码部分
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
NSMutableArray *mutablePairs = [NSMutableArray array];
//把参数给AFQueryStringPairsFromDictionary,拿到AF的一个类型的数据就一个key,value对象,在URLEncodedStringValue拼接keyValue,一个加到数组里
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
[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返回的是NSString,所以此处compare:使用的是NSString的compare函数
// 即@[@"dog", @"cat", @"dad"] ----> @[@"cat", @"dad",@"dog"]
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
//判断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;
}
以上转码方式主要通过判断value是否为dictionary,set,array,通过递归调用,解析到这三者以外的数据结构,再返回参数数组
这里面有个AFQueryStringPair
对象
@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id value;
- (instancetype)initWithField:(id)field value:(id)value;
- (NSString *)URLEncodedStringValue;
@end
@implementation AFQueryStringPair
- (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])];
}
}
解码过程可以由以下例子梳理一下
@{
@"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
到此为止,AF自制了一个request。
————————————————————————————————分割线———————————————————————————————————
回到核心方法
- (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) {
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;
}
我们可以看到如果解析不成功,AF直接调用failure这个block,里面有个self.completionQueue
自定义队列,如果设置了,那么就从这个队列中回调结果,否则就从主队列回调
接下来我们去父类AFURLSessionManger
看看了
- (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;
//第一件事,创建NSURLSessionDataTask,里面适配了Ios8以下taskIdentifiers,函数创建task对象。
//其实现应该是因为iOS 8.0以下版本中会并发地创建多个task对象,而同步有没有做好,导致taskIdentifiers 不唯
一…这边做了一个串行处理
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
函数,就传了一个Block进去,Block就是ios原生生成dataTask的方法
我们看下上面这个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
//理解下,第一为什么用sync,因为是想要主线程等在这,等执行完,在返回,因为必须执行完dataTask才有数 据,传值才有意义。
//第二,为什么要用串行队列,因为这块是为了防止ios8以下内部的dataTaskWithRequest是并发创建的,
这样会导致taskIdentifiers这个属性值不唯一,因为后续要用taskIdentifiers来作为Key对应delegate。
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;
}
为什么不直接调用dataTask = [self.session dataTaskWithRequest:request];
,原因就是iOS8以下,创建session的时候,偶发的情况会出现session的属性taskIdentifier的值不唯一,而这个taskIdentifier是我们后面来映射delegate的key,所以它必须是唯一的。至于为什么会不唯一,可能是因为NSURLSession内部生成task的时候是用多线程并发执行的,所以我们采用同步的方法
接下来我们继续看addDelegateForDataTask:
这个方法
- (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;
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
AF自定义了一个AFURLSessionManagerTaskDelegate
代理,持有我们请求的参数
并把delegate的manager传给了self,并传了一些block
至此,我们的dataTask就封装好了
****************************************分割线又来了*****************************************
接下我们来看代理实现
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
//根据task来取我们一开始绑定的delegate
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// delegate may be nil when completing a task in the background
if (delegate) {
//绑定的delegate再去调用系统的源方法
[delegate URLSession:session task:task didCompleteWithError:error];
//再移除delegate上绑定的通知
[self removeDelegateForTask:task];
}
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
这里就是task完成了的回调,这里先拿到绑定的delegate,然后调用NSURLSessionDelegate
的方法
代理1
//当这个session失效时,代理方法会被调用
- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error
{
if (self.sessionDidBecomeInvalid) {
self.sessionDidBecomeInvalid(session, error);
}
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}
这个代理就是当session失效,调用我们的自定义的Block,还发了一个失效的通知,这个通知的作用目前AF还没用到
代理2
//https认证
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
//挑战处理类型为 默认
/*
NSURLSessionAuthChallengePerformDefaultHandling:默认方式处理
NSURLSessionAuthChallengeUseCredential:使用指定的证书
NSURLSessionAuthChallengeCancelAuthenticationChallenge:取消挑战
*/
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
//此处服务器要求客户端的接收认证挑战方法是NSURLAuthenticationMethodServerTrust
//也就是说服务器端需要客户端返回一个根据认证挑战的保护空间提供的信任(即challenge.protectionSpace.serverTrust)产生的挑战证书。
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
这个代理作用是,验证客户端是否为正常用户,再决定是否返回真实数据,这就是服务端要求客户端接收挑战。接收挑战后,客户端要根据服务端传来的challenge来生成completionHandler需要的NSURLSessionAuthChallengeDisposition和NSURLCredential,前者是应对这个挑战的方法,后者是客户端生成的挑战证书,且challenge认证方法为NSURLAuthenticationMethodServerTrust的时候,才需要生成挑战证书,最后用completionHandler回应服务器端的挑战
NSURLSessionTaskDelegate
代理3
// 当session中所有已经入队的消息被发送出去后,会调用该代理方法。
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
if (self.didFinishEventsForBackgroundURLSession) {
dispatch_async(dispatch_get_main_queue(), ^{
self.didFinishEventsForBackgroundURLSession(session);
});
}
}
·在iOS中,当一个后台传输任务完成或者后台传输时需要证书,而此时你的app正在后台挂起,那么你的app在后台会自动重新启动运行,并且这个app的UIApplicationDelegate会发送一个application:handleEventsForBackgroundURLSession:completionHandler:消息。该消息包含了对应后台的session的identifier,而且这个消息会导致你的app启动。你的app随后应该先存储completion handler,然后再使用相同的identifier创建一个background configuration,并根据这个background configuration创建一个新的session。这个新创建的session会自动与后台任务重新关联在一起。
·当你的app获取了一个URLSessionDidFinishEventsForBackgroundURLSession:消息,这就意味着之前这个session中已经入队的所有消息都转发出去了,这时候再调用先前存取的completion handler是安全的,或者因为内部更新而导致调用completion handler也是安全的。
代理4
//被服务器重定向的时候调用
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest *))completionHandler
{
NSURLRequest *redirectRequest = request;
// step1. 看是否有对应的user block 有的话转发出去,通过这4个参数,返回一个NSURLRequest类型参数,request转发、网络重定向.
if (self.taskWillPerformHTTPRedirection) {
//用自己自定义的一个重定向的block实现,返回一个新的request。
redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
}
if (completionHandler) {
// step2. 用request重新请求
completionHandler(redirectRequest);
}
}
这个代理会在服务器重定向的时候调用,我们可以重新定义这个重定向的request
代理5
//https认证
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.taskDidReceiveAuthenticationChallenge) {
disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
disposition = NSURLSessionAuthChallengeUseCredential;
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
这个也是https认证,区别是这个是non-session-level级别,之前是session-level级别
相对于它,多了一个参数task,我们自定义的block也会多回传这个task作为参数,也可以根据这个task去定义我们需要的https认证方式
代理6
//当一个session task需要发送一个新的request body stream到服务器端的时候,调用该代理方法。
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
{
NSInputStream *inputStream = nil;
//有自定义的taskNeedNewBodyStream,用自定义的,不然用task里原始的stream
if (self.taskNeedNewBodyStream) {
inputStream = self.taskNeedNewBodyStream(session, task);
} else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
inputStream = [task.originalRequest.HTTPBodyStream copy];
}
if (completionHandler) {
completionHandler(inputStream);
}
}
该代理方法会在下面两种情况被调用:
·如果task是由uploadTaskWithStreamedRequest:创建的,那么提供初始的request body stream时候会调用该代理方法。
·因为认证挑战或者其他可恢复的服务器错误,而导致需要客户端重新发送一个含有body stream的request,这时候会调用该代理。
代理7
/*
//周期性地通知代理发送到服务器端数据的进度。
*/
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
// 如果totalUnitCount获取失败,就使用HTTP header中的Content-Length作为totalUnitCount
int64_t totalUnitCount = totalBytesExpectedToSend;
if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
if(contentLength) {
totalUnitCount = (int64_t) [contentLength longLongValue];
}
}
if (self.taskDidSendBodyData) {
self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
}
}
代理8
/*
task完成之后的回调,成功和失败都会回调这里
函数讨论:
注意这里的error不会报告服务期端的error,他表示的是客户端这边的eroor,比如无法解析hostname或者连不上host主机。
*/
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
//根据task去取我们一开始创建绑定的delegate
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// delegate may be nil when completing a task in the background
if (delegate) {
//把代理转发给我们绑定的delegate
[delegate URLSession:session task:task didCompleteWithError:error];
//转发完移除delegate
[self removeDelegateForTask:task];
}
//自定义Block回调
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
这个方法就是task完成的回调,获取了这个task绑定的delegate,转发代理,并且取消绑定,移除通知
NSURLSessionDataDelegate
代理9
//收到服务器响应后调用
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
//设置默认为继续进行
NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
//自定义去设置
if (self.dataTaskDidReceiveResponse) {
disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
}
if (completionHandler) {
completionHandler(disposition);
}
}
·当你把添加content-type的类型为multipart/x-mixed-replace那么服务器的数据会分片的传回来。然后这个方法是每次接受到对应片响应的时候会调被调用。
·如果我们实现了自定义Block,则调用一下,不然就用默认的NSURLSessionResponseAllow方式。
代理10
//上面的代理中disposition如果设置为NSURLSessionResponseBecomeDownload,则会调用这个方法
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
{
//因为转变了task,所以要对task做一个重新绑定
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
if (delegate) {
[self removeDelegateForTask:dataTask];
[self setDelegate:delegate forTask:downloadTask];
}
//执行自定义Block
if (self.dataTaskDidBecomeDownloadTask) {
self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
}
}
这个代理就是被上面的方法触发,作用就是新建一个downloadTask,替换掉当前dataTask,所以我们在这里做了AF自定义代理的重新绑定操作
调用自定义的代理
代理11
//当我们获取到数据就会调用,会被反复调用,请求到的数据就在这被拼装完整
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
[delegate URLSession:session dataTask:dataTask didReceiveData:data];
if (self.dataTaskDidReceiveData) {
self.dataTaskDidReceiveData(session, dataTask, data);
}
}
这个方法到AF的代理中去,所以数据的拼接都是在AF的代理进行
代理12
/*当task接收到所有期望的数据后,session会调用此代理方法。
*/
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
willCacheResponse:(NSCachedURLResponse *)proposedResponse
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
{
NSCachedURLResponse *cachedResponse = proposedResponse;
if (self.dataTaskWillCacheResponse) {
cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);
}
if (completionHandler) {
completionHandler(cachedResponse);
}
}
缓存response的方法,我们自定义了一个response来缓存
NSURLSessionDownloadDelegate
代理13
//下载完成的时候调用
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
//这个是session的,也就是全局的,后面的个人代理也会做同样的这件事
if (self.downloadTaskDidFinishDownloading) {
//调用自定义的block拿到文件存储的地址
NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (fileURL) {
delegate.downloadFileURL = fileURL;
NSError *error = nil;
//从临时的下载路径移动至我们需要的路径
[[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error];
//如果移动出错
if (error) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
}
return;
}
}
//转发代理
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
}
}
这个方法和之前的两个方法
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)taskdidCompleteWithError:(NSError *)error;
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data;
总共3个方法,被转到AF自定义的delegate中了
代理14
//周期性地通知下载进度调用
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
if (self.downloadTaskDidWriteData) {
self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}
}
简单说一下这几个参数:
bytesWritten 表示自上次调用该方法后,接收到的数据字节数
totalBytesWritten表示目前已经接收到的数据字节数
totalBytesExpectedToWrite 表示期望收到的文件总字节数,是由Content-Length header提供。如果没有提供,默认是NSURLSessionTransferSizeUnknown。
代理15
//当下载被取消或者失败后重新恢复下载时调用
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
//交给自定义的Block去调用
if (self.downloadTaskDidResume) {
self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes);
}
}
·其实这个就是用来做断点续传的代理方法。可以在下载失败的时候,拿到我们失败的拼接的部分resumeData,然后用去调用downloadTaskWithResumeData:就会调用到这个代理方法来了。
·其中注意:fileOffset这个参数,如果文件缓存策略或者最后文件更新日期阻止重用已经存在的文件内容,那么该值为0。否则,该值表示当前已经下载data的偏移量。
方法中仅仅调用了downloadTaskDidResume自定义Block。
至此,NSURLSessionDelagte就讲完了,总结如下
·每个代理方法对应一个我们自定义的Block,如果Block被赋值了,那么就调用它。
·在这些代理方法里,我们做的处理都是相对于这个sessionManager所有的request的。是公用的处理。
·转发了3个代理方法到AF的deleagate中去了,AF中的deleagate是需要对应每个task去私有化处理的。
我们继续来看被转发的AF代理
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
//因为self.manager被声明为weak,所以需要强引用
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
NSData *data = nil;
if (self.mutableData) {
data = [self.mutableData copy];
//不需要这个值之后,释放内存
self.mutableData = nil;
}
//继续给userInfo填数据
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
if (error) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, error);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
} else {
dispatch_async(url_session_manager_processing_queue(), ^{
NSError *serializationError = nil;
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
//如果是下载文件,那么responseObject为下载路径
if (self.downloadFileURL) {
responseObject = self.downloadFileURL;
}
//写入userInfo
if (responseObject) {
userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
}
//如果解析错误
if (serializationError) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
}
//回调结果
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, serializationError);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
});
}
}
这个代理生成了一个储存task相关信息的字典:userInfo
,这个字典是用来发送任务完成的通知的参数
判断error,是否请求成功或失败
如果成功则在AF的并行queue中,做数据解析(耗时)的后续操作
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
我们点进去这个方法看看
@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
@end
AF定义了一个AFURLResponseSerialization类,遵守协议方法来解析我们请求到的data转换为我们需要的类型的数据的方法。
AF还判断了,如果定义了GCD完成组completionGroup
和完成队列的话completionQueue
,会在加入这个组合队列中回调Block。否则默认是AF创建的组
和主队列回调。
最后回到主线程发送通知
AF代理2
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
//拼接数据
[self.mutableData appendData:data];
}
同样NSURLSession代理被转发到这里,拼接了需要回调的数据
AF代理3
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
NSError *fileManagerError = nil;
self.downloadFileURL = nil;
//AF代理的自定义Block
if (self.downloadTaskDidFinishDownloading) {
//得到自定义下载路径
self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (self.downloadFileURL) {
//把下载路径移动到我们自定义的下载路径
[[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];
//错误发通知
if (fileManagerError) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
}
}
}
}
·下载成功后会被NSURLSession代理转发到这里,之前的代理和这里都移动了文件到下载路径,而NSUrlSession代理的下载路径是所有request公用的下载路径,一旦设置,所有的request都会下载到之前那个路径。
·而这个是对应的每个task的,每个task可以设置各自下载路径,还记得AFHttpManager的download方法么
[manager downloadTaskWithRequest:resquest progress:nil destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
return path;
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
}];
这个地方return的path就是对应的这个代理方法里面的path
接下来我们来说明之前的AFURLResponseSerialization
这一块是如何解析数据的
image.png
这6个类都是遵守这个协议方法,去做数据解析
AFHTTPResponseSerializer:
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
[self validateResponse:(NSHTTPURLResponse *)response data:data error:error];
return data;
}
往里看这个方法
// 判断是不是可接受类型和可接受code,不是则填充error
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
//response是否合法标识
BOOL responseIsValid = YES;
//验证的error
NSError *validationError = nil;
//如果存在且是NSHTTPURLResponse
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
//主要判断自己能接受的数据类型和response的数据类型是否匹配,
//如果有接受数据类型,如果不匹配response,而且响应类型不为空,数据长度不为0
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
!([response MIMEType] == nil && [data length] == 0)) {
//进入If块说明解析数据肯定是失败的,这时候要把解析错误信息放到error里。
//如果数据长度大于0,而且有响应url
if ([data length] > 0 && [response URL]) {
//错误信息字典,填充一些错误信息
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
//生成错误
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
}
//返回标识
responseIsValid = NO;
}
//判断自己可接受的状态吗
//如果和response的状态码不匹配,则进入if块
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
//填写错误信息字典
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
//生成错误
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
//返回标识
responseIsValid = NO;
}
}
//给我们传过来的错误指针赋值
if (error && !responseIsValid) {
*error = validationError;
}
//返回是否错误标识
return responseIsValid;
}
看注释应该很容易明白这个方法有什么作用。简单来说,这个方法就是来判断返回数据与咱们使用的解析器是否匹配,需要解析的状态码是否匹配。如果错误,则填充错误信息,并且返回NO,否则返回YES,错误信息为nil。
其中里面出现了两个属性值,一个acceptableContentTypes,一个acceptableStatusCodes,两者在初始化的时候有给默认值,我们也可以去自定义,但是如果给acceptableContentTypes定义了不匹配的类型,那么数据仍旧会解析错误。
而AFHTTPResponseSerializer仅仅是调用验证方法,然后就返回了data。
AFJSONResponseSerializer:
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
//先判断是不是可接受类型和可接受code
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
//error为空,或者有错误,去函数里判断。
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
//返回空
return nil;
}
}
id responseObject = nil;
NSError *serializationError = nil;
// Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
// See https://github.com/rails/rails/issues/1742
//如果数据为空
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
//不空则去json解析
if (data.length > 0 && !isSpace) {
responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
} else {
return nil;
}
//判断是否需要移除Null值
if (self.removesKeysWithNullValues && responseObject) {
responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
//拿着json解析的error去填充错误信息
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
//返回解析结果
return responseObject;
}
里面用到了3个函数
//1
AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain))
//2
AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
//3
AFErrorWithUnderlyingError(serializationError, *error);
我们看第一个
//判断是不是我们自己之前生成的错误信息,是的话返回YES
static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
//判断错误域名和传过来的域名是否一致,错误code是否一致
if ([error.domain isEqualToString:domain] && error.code == code) {
return YES;
}
//如果userInfo的NSUnderlyingErrorKey有值,则在判断一次。
else if (error.userInfo[NSUnderlyingErrorKey]) {
return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);
}
return NO;
}
这里可以注意,我们这里传过去的code和domain两个参数分别为NSURLErrorCannotDecodeContentData
、AFURLResponseSerializationErrorDomain
,这两个参数是我们之前判断response可接受类型和code时候自己去生成错误的时候填写的。
第二个
static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
//分数组和字典
if ([JSONObject isKindOfClass:[NSArray class]]) {
//生成一个数组,只需要JSONObject.count个
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
for (id value in (NSArray *)JSONObject) {
//调用自己
[mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
}
//看我们解析类型是mutable还是非muatable,返回mutableArray或者array
return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
} else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];
for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) {
id value = (NSDictionary *)JSONObject[key];
//value空则移除
if (!value || [value isEqual:[NSNull null]]) {
[mutableDictionary removeObjectForKey:key];
} else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
//如果数组还是去调用自己
mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);
}
}
return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
return JSONObject;
}
第三个
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
if (!error) {
return underlyingError;
}
if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
return error;
}
NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
}
最后做个总结
请求网络是由NSUrlSession来做的,它内部维护了一个线程池,用来做网络请求。它调度线程,基于底层的CFSocket去发送请求和接受数据,这些线程都是并发的。
我们一开始初始化sessionManager
的时候,一般都在主线程
当我们去调用get或post去请求数据,接着会进行request拼接,AF代理的字典映射,progress的KVO添加等等,到NSUrlSession的resume之前的准备工作,仍旧是在主线程中的
然后我们调用NSUrlSession的resume,接着跑到NSUrlSession内部去对网络进行数据请求了,在它内部是多线程并发的请求数据
数据请求完成后,回调回来再我们一开始生成的并发数为1的NSOperationQueue中,这个时候回事多线程串行的回调回来的
然后我们创建并发的多线程,去对这些数据进行各种解析
最后我们如果有自定义的completionQueue,则在自定义的queue中回调回来,也就是分线程回调回来,否则就是主队列,主线程中回调结束。
网友评论