AFNetworking 窥探 (一)

一、前言

最近有些时间了, 于是就开始看看 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;
}

三、总结

在这实现过程中他还帮我们做了开线程,异步下载,检查数据是否合法,图片解压等一些繁琐的事情,所以我们才能用得这么舒心,但是我们还是有必要研究一下内部的实现,只有保持着一颗求知的心,我们才能研究的更深,得到的更多.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,475评论 4 372
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,744评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,101评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,732评论 0 221
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,141评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,049评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,188评论 2 320
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,965评论 0 213
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,716评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,867评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,341评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,663评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,376评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,200评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,990评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,179评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,979评论 2 279

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,112评论 18 139
  • 写在开头: 作为一个iOS开发,也许你不知道NSUrlRequest、不知道NSUrlConnection、也不知...
    涂耀辉阅读 98,437评论 172 1,344
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,021评论 11 349
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,598评论 25 707
  • 成为领导的得意员工,必然会在升职提薪、资源方面得到更多的照顾,做起事情来,也会更加顺心顺手。然而领导带领的下属那么...
    无顾有长风阅读 778评论 0 0