一、前言
最近有些时间了, 于是就开始看看 AFNetworking. 写一篇文章, 当做一种自我总结.
二、简单了解
1.模块
进入到 AFNetworking 里面, 除掉 Support Files, 可分为五个功能模块.
- 网络通信模块(Session)
- 网络状态监听模块(Reachability)
- 网络通讯安全策略模块(Security)
- 网络通讯信息序列化/反序列化模块(Serialization)
- UIKit库的扩展(UIKit)
序列化
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为对象。
把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化。
说的再直接点,序列化的目的就是为了跨进程传递格式化数据
2.数据请求核心
数据请求核心是网络通信模块的 AFURLSessionManager. AFNetworking3是机遇 NSURLSession来封装的. 所以这个类围绕着NSURLSession 做了一系列的封装.而其余的四个模块,均是为了配合网络通信或对已有UIKit的一个扩展工具包.
AF架构图其中 AFHTTPSessionManager 是继承于 NSURLSessionManager, 但是它本身没有做实事的, 只是把一些请求逻辑分发给父类AFURLSessionManager
流程图
3. get 请求
我们先简单的写一个 get 请求.
//使用代码
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager GET:url parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
// 成功
NSDictionary *obj = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:nil];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
// 失败
}];
3.1 初始化
首先我们调用了一个初始化方法 init 生成了一个 manager.
- 初始化方法都调用了,- (instancetype)initWithBaseURL:(NSURL *)url sessionConfiguration:(NSURLSessionConfiguration *)configuration;
- 初始化方法都调用了父类的初始化方法.
- 方法把 baseURL 保存了起来, 生成了一个请求序列对象和一个响应序列对象.
- (instancetype)init {
return [self initWithBaseURL:nil];
}
- (instancetype)initWithBaseURL:(NSURL *)url {
return [self initWithBaseURL:url sessionConfiguration:nil];
}
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
return [self initWithBaseURL:nil sessionConfiguration:configuration];
}
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
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)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 的字典
// 重点!! 在AFNetwroking中, 每一个 task 都会被匹配一个 AFURLSessionManagerTaskDeleDalegate 来做 task 的 delegate 事件处理
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
// AFURLSessionManagerTaskDeleDalegate 字典锁, 确保地点在多线程访问时的线程安全.
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.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) ];
关于这个方法,异步获取当前 session 的所有未完成的 task, 在里面把里面绑定的代理什么的有关的东西都置空了, 在你刚刚初始化的时候, 其实是没有 task, 而初始化的时候把之前里面绑定的东西都置为空, 应该是一种防御性变成的提现. 例如,我重复去初始化 session 的时候, 会有新的 session 指向旧的未完成的 task 的 session.
初始化方法到这里就先告一段落了.
3.2网络请求
下面我们来看一些网络请求的东西
- (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;
}
方法中生成一个 NSURLSessionDataTask 的实例, 并且开始网络请求.点开方法,继续往里面走.
- (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;
}
这个方法中完成了俩件事情,
- self.requestSerializer, method, URLString , 合成一个 request 请求.
- 根据 request 请求拿到 dataTask 并且返回回去, 成功失败的方法也在 block 里面回调.
对下面这个有兴趣可以看看这个⚠️编译错误
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
#pragma clang diagnostic pop
点开 requset 生成方法继续看下去
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
- (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;
// 将 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) 确定请求方式 get, post, put 等等
- (2) 在 request 里添加一些参数设置, 其中 AFHTTPRequestSerializerObservedKeyPaths() 是一个 C函数, 返回一个数组, 我们继续进去看看呀.
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 此处需要observer的keypath为allowsCellularAccess、cachePolicy、HTTPShouldHandleCookies
// HTTPShouldUsePipelining、networkServiceType、timeoutInterval
_AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
});
// 就是一个数组里面装着很多方法的名字,
return _AFHTTPRequestSerializerObservedKeyPaths;
}
这个函数就是封装了一些属性的名字, 这些都是 NSUrlRequest 的属性.
然后这个self.mutableObservedChangedKeyPaths
属性, 就是当前类和 NSUrlRequest 的属性.
@interface AFHTTPRequestSerializer ()
@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;
在 - init 方法里面对这个集合进行了初始化, 并且对当前类和 NSUrlRequest 相关的那些属性添加了 KVO 监听
// 每次都会重置变化
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 触发的方法
- (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 属性值得集合.
接下来[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
的方法,利用 KVO 把属性值传到 request.
- (3) 把需要传递的参数进行编码, 并且设置到 request 中去.
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
#pragma mark - AFURLRequestSerialization
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
// 将传入的 parameters 进行编码, 并添加到 request 中
[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 的 query 是拼接到 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;
}
这个方法做了三件事情.
-
self.HTTPRequestHeaders
拿到设置参数, 赋值要请求的 request 里面去 - 把请求的网络的参数, 从 array dic set 这些容器类型转换为字符串, 具体转码方式, 我们可以使用自定义的方式, 也可以用 AF 默认的转码方式. 自定义的转码方式根据自己想怎么解析来决定. 我们可以看看默认的方式:
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;
转码主要是以上三个函数, 配合着注释理解, 主要是在递归调用AFQueryStringPairsFromKeyAndValue
. 判断vaLue 是什么类型, 然后去递归调用自己, 直到解析的是除了 array dic set 以外的元素, 然后把得到的参数数组返回.
其中有一个AFQueryStringPair
对象, 其只有俩个属性和俩个方法:
@interface AFQueryStringPair : NSObject
@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])];
}
}
@end
方法很简单, 现在我们也开始理解这个转码过程了, 我们举个例子梳理下, 就是下面的这三步:
@{
@"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 (都包含在 HTTPMethodsEncodingParametersInURI), 因为这几个 method 的 query 是拼接到 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;
}
到这里我们就生成了一个 request.
看了这些, 我们再回到 AFHTTPSessionManager 类中来, 回到这个方法.
- (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中回调结果,否则从主队列中回调.
然后调用了父类生成task的方法, 并且自信了一个成功和失败的回调, 我们接着去父类的 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;
}
三、总结
在这实现过程中他还帮我们做了开线程,异步下载,检查数据是否合法,图片解压等一些繁琐的事情,所以我们才能用得这么舒心,但是我们还是有必要研究一下内部的实现,只有保持着一颗求知的心,我们才能研究的更深,得到的更多.
网友评论