美文网首页
AFNetworking 窥探 (一)

AFNetworking 窥探 (一)

作者: 学而不思则罔思而不学则殆 | 来源:发表于2017-04-05 17:04 被阅读35次

    一、前言

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

    三、总结

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

    相关文章

      网友评论

          本文标题:AFNetworking 窥探 (一)

          本文链接:https://www.haomeiwen.com/subject/ovkxattx.html