读读 AFNetworking Posted 2023-01-11 想名真难
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了读读 AFNetworking相关的知识,希望对你有一定的参考价值。
以前看过源码,但是看得很迷,还是这篇文章好,作者很厉害,其他文章也可以看看,原文(是一系列文章,此处是第一篇,后续直接看原文就好) : AFNetworking到底做了什么? - 简书
理解了 NSURLSession 能更好的理解AFN. NSURLSession
写在开头:
本文将从源码的角度去分析AF的实际作用。或许看完这篇文章,你心里会有一个答案。
先从最新的AF3.x讲起吧:
将AF下载导入工程后,下面是其包结构,相对于2.x变得非常简单了:
AF代码结构图.png
除去Support Files,可以看到AF分为如下5个功能模块:
其核心当然是网络通信模块NSURLSessionManager。大家都知道,AF3.x是基于NSURLSession来封装的。所以这个类围绕着NSURLSession做了一系列的封装。而其余的四个模块,均是为了配合网络通信或对已有UIKit的一个扩展工具包。
这五个模块所对应的类的结构关系图如下所示:
AF架构图.png
其中AFHTTPSessionManager是继承于NSURLSessionManager的,我们一般做网络请求都是用AFHTTPSessionManager,但是它本身是没有做实事的,只是把一些请求逻辑分发给父类AFURLSessionManager或者其它类去做。
首先我们简单的写个get请求:
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc]init];
[manager GET:@"http://localhost" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject)
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error)
];
首先我们我们调用了初始化方法生成了一个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;
//对传过来的BaseUrl进行处理,截去url最后的/
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"])
url = [url URLByAppendingPathComponent:@""];
self.baseURL = url;
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
直接来到父类AFURLSessionManager的初始化方法:
- (instancetype)init
return [self initWithSessionConfiguration:nil];
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration
self = [super init];
if (!self)
return nil;
if (!configuration)
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
self.sessionConfiguration = configuration;
self.operationQueue = [[NSOperationQueue alloc] init];
//queue并发线程数设置为1
self.operationQueue.maxConcurrentOperationCount = 1;
//注意代理,代理的继承,实际上NSURLSession去判断了,你实现了哪个方法会去调用,包括子代理的方法!
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的词典(重点,在AFNet中,每一个task都会被匹配一个AFURLSessionManagerTaskDelegate 来做task的delegate事件处理) ===============
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
// 设置AFURLSessionManagerTaskDelegate 词典的锁,确保词典在多线程访问时的线程安全
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
// 置空task关联的代理
[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;
self.operationQueue.maxConcurrentOperationCount = 1;这个operationQueue就是我们代理回调的queue。这里把代理回调的线程并发数设置为1了。至于这里为什么要这么做,我们先留一个坑,等我们讲完AF2.x之后再来分析这一块。
第二就是我们初始化了一些属性,其中包括self.mutableTaskDelegatesKeyedByTaskIdentifier,这个是用来让每一个请求task和我们自定义的AF代理来建立映射用的,其实AF对task的代理进行了一个封装,并且转发代理到AF自定义的代理,这是AF比较重要的一部分,接下来我们会具体讲这一块。
第三就是下面这个方法:
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks)
];
首先说说这个方法是干什么用的:这个方法用来异步的获取当前session的所有未完成的task。其实讲道理来说在初始化中调用这个方法应该里面一个task都不会有。我们打断点去看,也确实如此,里面的数组都是空的。但是想想也知道,AF大神不会把一段没用的代码放在这吧。所以我大胆猜测,可能是当我们重复去初始化session的时候(当然我们实际也不会这么做),会有新的session指向旧的有未完成task的session。为了排除这种不确定性因素,所以在初始化的时候把里面绑定的代理之类的东西都置为nil了。或许这就是防御性编程思想的一种体现吧。
初始化方法到这就全部完成了。
接着我们来看看网络请求:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (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
//生成一个task
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@
"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
//开始网络请求
[dataTask resume];
return
dataTask;
方法走到类AFHTTPSessionManager中来,调用父类,也就是我们整个AF3.x的核心类AFURLSessionManager的方法,生成了一个系统的NSURLSessionDataTask实例,并且开始网络请求。
我们继续往父类里看,看看这个方法到底做了什么:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
- (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];
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;
1.用self.requestSerializer和各种参数去获取了一个我们最终请求网络需要的NSMutableURLRequest实例。
2.调用另外一个方法dataTaskWithRequest去拿到我们最终需要的NSURLSessionDataTask实例,并且在完成的回调里,调用我们传过来的成功和失败的回调。
1
2
3
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
#pragma clang diagnostic pop
这里是用来忽略:?带来的警告,具体的各种编译器警告描述,可以参考这篇:各种编译器的警告。
接着我们跑到AFURLRequestSerialization类中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
//断言,debug模式下,如果缺少改参数,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];
//将传入的parameters进行编码,并添加到request中
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return
mutableRequest;
1)设置request的请求类型,get,post,put...等
2)往request里添加一些参数设置,其中AFHTTPRequestSerializerObservedKeyPaths()是一个c函数,返回一个数组,我们来看看这个函数:
1
2
3
4
5
6
7
8
9
10
11
static NSArray * AFHTTPRequestSerializerObservedKeyPaths()
static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
static dispatch_once_t onceToken;
// 此处需要observer的keypath为allowsCellularAccess、cachePolicy、HTTPShouldHandleCookies
// HTTPShouldUsePipelining、networkServiceType、timeoutInterval
dispatch_once(&onceToken, ^
_AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
);
//就是一个数组里装了很多方法的名字,
return
_AFHTTPRequestSerializerObservedKeyPaths;
其实这个函数就是封装了一些属性的名字,这些都是NSUrlRequest的属性。
再来看看self.mutableObservedChangedKeyPaths,这个是当前类的一个属性:
1
@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;
在-init方法对这个集合进行了初始化,并且对当前类的和NSUrlRequest相关的那些属性添加了KVO监听:
1
2
3
4
5
6
7
8
//每次都会重置变化
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];
KVO触发的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(__unused id)object
change:(NSDictionary *)change
context:(void *)context
//当观察到这些set方法被调用了,而且不为Null就会添加到集合里,否则移除
if
(context == AFHTTPRequestSerializerObserverContext)
if
([change[NSKeyValueChangeNewKey] isEqual:[NSNull
null
]])
[self.mutableObservedChangedKeyPaths removeObject:keyPath];
else
[self.mutableObservedChangedKeyPaths addObject:keyPath];
至此我们知道self.mutableObservedChangedKeyPaths其实就是我们自己设置的request属性值的集合。
接下来调用:
1
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
用KVC的方式,把属性值都设置到我们请求的request中去。
3)把需要传递的参数进行编码,并且设置到request中去:
//将传入的parameters进行编码,并添加到request中
1
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
//从自己的head里去遍历,如果有值则设置给request的head
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop)
if
(![request valueForHTTPHeaderField:field])
[mutableRequest setValue:value forHTTPHeaderField:field];
];
//来把各种类型的参数,array dic set转化成字符串,给request
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
//post put请求
// #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.从self.HTTPRequestHeaders中拿到设置的参数,赋值要请求的request里去
2.把请求网络的参数,从array dic set这些容器类型转换为字符串,具体转码方式,我们可以使用自定义的方式,也可以用AF默认的转码方式。自定义的方式没什么好说的,想怎么去解析由你自己来决定。我们可以来看看默认的方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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函数
// 即@[@"foo", @"bar", @"bae"] ----> @[@"bae", @"bar",@"foo"]
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@
"description"
ascending:YES selector:@selector(compare:)];
//判断vaLue是什么类型的,然后去递归调用自己,直到解析的是除了array dic set以外的元素,然后把得到的参数数组返回。
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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id 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])];
方法很简单,现在我们也很容易理解这整个转码过程了,我们举个例子梳理下,就是以下这3步:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@
@
"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中请求类型,来判断参数字符串应该如何设置到request中去。如果是GET、HEAD、DELETE,则把参数quey是拼接到url后面的。而POST、PUT是把query拼接到http body中的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
//post put请求
// #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]];
至此,我们生成了一个request。
我们再回到AFHTTPSessionManager类中来,回到这个方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
- (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];
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;
绕了一圈我们又回来了。。
我们继续往下看:当解析错误,我们直接调用传进来的fauler的Block失败返回了,这里有一个self.completionQueue,这个是我们自定义的,这个是一个GCD的Queue如果设置了那么从这个Queue中回调结果,否则从主队列回调。
实际上这个Queue还是挺有用的,之前还用到过。我们公司有自己的一套数据加解密的解析模式,所以我们回调回来的数据并不想是主线程,我们可以设置这个Queue,在分线程进行解析数据,然后自己再调回到主线程去刷新UI。
言归正传,我们接着调用了父类的生成task的方法,并且执行了一个成功和失败的回调,我们接着去父类AFURLSessionManger里看(总算到我们的核心类了..):
1
2
3
4
5
6
7
8
9
10
11
12
13
- (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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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:When tasks are created on a concurrent queue, incorrect completionHandlers can get called. · Issue #2093 · AFNetworking/AFNetworking · GitHub
//理解下,第一为什么用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;
1
dataTask = [self.session dataTaskWithRequest:request];
非要绕这么一圈,我们点进去bug日志里看看,原来这是为了适配iOS8的以下,创建session的时候,偶发的情况会出现session的属性taskIdentifier这个值不唯一,而这个taskIdentifier是我们后面来映射delegate的key,所以它必须是唯一的。
我们接着看到:
1
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
调用到:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (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] init];
// AFURLSessionManagerTaskDelegate与AFURLSessionManager建立相互关系
delegate.manager = self;
delegate.completionHandler = completionHandler;
//这个taskDescriptionForSessionTasks用来发送开始和挂起通知的时候会用到,就是用这个值来Post通知,来两者对应
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
// ***** 将AF delegate对象与 dataTask建立关系
[self setDelegate:delegate forTask:dataTask];
// 设置AF delegate的上传进度,下载进度块。
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
总结一下:
1)这个方法,生成了一个AFURLSessionManagerTaskDelegate,这个其实就是AF的自定义代理。我们请求传来的参数,都赋值给这个AF的代理了。
2)delegate.manager = self;代理把AFURLSessionManager这个类作为属性了,我们可以看到:
1
@property (nonatomic, weak) AFURLSessionManager *manager;
这个属性是弱引用的,所以不会存在循环引用的问题。
3)我们调用了[self setDelegate:delegate forTask:dataTask];
我们进去看看这个方法做了什么:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
//断言,如果没有这个参数,debug下crash在这
NSParameterAssert(task);
NSParameterAssert(delegate);
//加锁保证字典线程安全
[self.lock lock];
// 将AF delegate放入以taskIdentifier标记的词典中(同一个NSURLSession中的taskIdentifier是唯一的)
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
// 为AF delegate 设置task 的progress监听
[delegate setupProgressForTask:task];
//添加task开始和暂停的通知
[self addNotificationObserverForTask:task];
[self.lock unlock];