AFNetworking 窺探 (一)

一、前言

最近有些時(shí)間了, 于是就開始看看 AFNetworking. 寫一篇文章, 當(dāng)做一種自我總結(jié).


二、簡(jiǎn)單了解

1.模塊

進(jìn)入到 AFNetworking 里面, 除掉 Support Files, 可分為五個(gè)功能模塊.


  • 網(wǎng)絡(luò)通信模塊(Session)
  • 網(wǎng)絡(luò)狀態(tài)監(jiān)聽模塊(Reachability)
  • 網(wǎng)絡(luò)通訊安全策略模塊(Security)
  • 網(wǎng)絡(luò)通訊信息序列化/反序列化模塊(Serialization)
  • UIKit庫(kù)的擴(kuò)展(UIKit)
序列化

當(dāng)兩個(gè)進(jìn)程在進(jìn)行遠(yuǎn)程通信時(shí),彼此可以發(fā)送各種類型的數(shù)據(jù)。無(wú)論是何種類型的數(shù)據(jù),都會(huì)以二進(jìn)制序列的形式在網(wǎng)絡(luò)上傳送。發(fā)送方需要把這個(gè)對(duì)象轉(zhuǎn)換為字節(jié)序列,才能在網(wǎng)絡(luò)上傳送;接收方則需要把字節(jié)序列再恢復(fù)為對(duì)象。
把對(duì)象轉(zhuǎn)換為字節(jié)序列的過(guò)程稱為對(duì)象的序列化。
把字節(jié)序列恢復(fù)為對(duì)象的過(guò)程稱為對(duì)象的反序列化。
說(shuō)的再直接點(diǎn),序列化的目的就是為了跨進(jìn)程傳遞格式化數(shù)據(jù)

2.數(shù)據(jù)請(qǐng)求核心

數(shù)據(jù)請(qǐng)求核心是網(wǎng)絡(luò)通信模塊的 AFURLSessionManager. AFNetworking3是機(jī)遇 NSURLSession來(lái)封裝的. 所以這個(gè)類圍繞著NSURLSession 做了一系列的封裝.而其余的四個(gè)模塊,均是為了配合網(wǎng)絡(luò)通信或?qū)σ延蠻IKit的一個(gè)擴(kuò)展工具包.

AF架構(gòu)圖

其中 AFHTTPSessionManager 是繼承于 NSURLSessionManager, 但是它本身沒(méi)有做實(shí)事的, 只是把一些請(qǐng)求邏輯分發(fā)給父類AFURLSessionManager


流程圖

3. get 請(qǐng)求

我們先簡(jiǎn)單的寫一個(gè) get 請(qǐng)求.
//使用代碼
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 初始化
首先我們調(diào)用了一個(gè)初始化方法 init 生成了一個(gè) manager.
  • 初始化方法都調(diào)用了,- (instancetype)initWithBaseURL:(NSURL *)url sessionConfiguration:(NSURLSessionConfiguration *)configuration;
  • 初始化方法都調(diào)用了父類的初始化方法.
  • 方法把 baseURL 保存了起來(lái), 生成了一個(gè)請(qǐng)求序列對(duì)象和一個(gè)響應(yīng)序列對(duì)象.
- (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;
}
讓我們來(lái)到父類 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 并發(fā)線程數(shù)設(shè)置為 1
    self.operationQueue.maxConcurrentOperationCount = 1;
    // 注意代理, 代理的繼承, 實(shí)際上 NSURLSession 去判斷了, 你實(shí)現(xiàn)了哪個(gè)方法回去調(diào)用,包括子代理的方法.
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
    // 各種響應(yīng)轉(zhuǎn)碼
    self.responseSerializer = [AFJSONResponseSerializer serializer];
    // 設(shè)置默認(rèn)的安全策略
    self.securityPolicy = [AFSecurityPolicy defaultPolicy];

#if !TARGET_OS_WATCH
    // 網(wǎng)絡(luò)狀態(tài)監(jiān)測(cè)
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
    // 設(shè)置儲(chǔ)存 NSURL task 與 AFURLSessionManagerTaskDelegate 的字典
    // 重點(diǎn)!! 在AFNetwroking中, 每一個(gè) task 都會(huì)被匹配一個(gè) AFURLSessionManagerTaskDeleDalegate 來(lái)做 task 的 delegate 事件處理
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    // AFURLSessionManagerTaskDeleDalegate 字典鎖, 確保地點(diǎn)在多線程訪問(wèn)時(shí)的線程安全.
    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;
    // 置空 task 關(guān)聯(lián)的代理
    [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;
}
這個(gè)就是最終的初始化方法了, 里面寫了一些注釋,現(xiàn)在還有一點(diǎn)要補(bǔ)充:
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) ];

關(guān)于這個(gè)方法,異步獲取當(dāng)前 session 的所有未完成的 task, 在里面把里面綁定的代理什么的有關(guān)的東西都置空了, 在你剛剛初始化的時(shí)候, 其實(shí)是沒(méi)有 task, 而初始化的時(shí)候把之前里面綁定的東西都置為空, 應(yīng)該是一種防御性變成的提現(xiàn). 例如,我重復(fù)去初始化 session 的時(shí)候, 會(huì)有新的 session 指向舊的未完成的 task 的 session.
初始化方法到這里就先告一段落了.

3.2網(wǎng)絡(luò)請(qǐng)求
下面我們來(lái)看一些網(wǎng)絡(luò)請(qǐng)求的東西
- (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
{
    // 生成一個(gè) task
    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];
    // 開始網(wǎng)絡(luò)請(qǐng)求
    [dataTask resume];

    return dataTask;
}

方法中生成一個(gè) NSURLSessionDataTask 的實(shí)例, 并且開始網(wǎng)絡(luò)請(qǐng)求.點(diǎn)開方法,繼續(xù)往里面走.

- (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;
    // 把參數(shù)轉(zhuǎn)化成一個(gè)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"
            // 如果解析錯(cuò)誤, 直接返回
            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;
}
這個(gè)方法中完成了倆件事情,
  • self.requestSerializer, method, URLString , 合成一個(gè) request 請(qǐng)求.
  • 根據(jù) request 請(qǐng)求拿到 dataTask 并且返回回去, 成功失敗的方法也在 block 里面回調(diào).
對(duì)下面這個(gè)有興趣可以看看這個(gè)??編譯錯(cuò)誤
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
#pragma clang diagnostic pop

點(diǎn)開 requset 生成方法繼續(xù)看下去

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 的各種屬性循環(huán)遍歷
   for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
       // 如果自己觀察到的發(fā)生變化的屬性, 在這些方法里
       if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
           // 把自己設(shè)置的屬性給 request 設(shè)置
           [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
       }
   }
   // 將傳入的 parameters 進(jìn)行編碼, 并添加到 request
   mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

   return mutableRequest;
}
這個(gè)方法里做了三件事情
  • (1) 確定請(qǐng)求方式 get, post, put 等等
  • (2) 在 request 里添加一些參數(shù)設(shè)置, 其中 AFHTTPRequestSerializerObservedKeyPaths() 是一個(gè) C函數(shù), 返回一個(gè)數(shù)組, 我們繼續(xù)進(jìn)去看看呀.
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))];
    });
    // 就是一個(gè)數(shù)組里面裝著很多方法的名字, 
    return _AFHTTPRequestSerializerObservedKeyPaths;
}

這個(gè)函數(shù)就是封裝了一些屬性的名字, 這些都是 NSUrlRequest 的屬性.

然后這個(gè)self.mutableObservedChangedKeyPaths屬性, 就是當(dāng)前類和 NSUrlRequest 的屬性.

@interface AFHTTPRequestSerializer ()
@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;

在 - init 方法里面對(duì)這個(gè)集合進(jìn)行了初始化, 并且對(duì)當(dāng)前類和 NSUrlRequest 相關(guān)的那些屬性添加了 KVO 監(jiān)聽

 // 每次都會(huì)重置變化
 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 觸發(fā)的方法

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(__unused id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    // 當(dāng)觀察到這些set方法被調(diào)用了, 而且不是 Null 就會(huì)添加到集合里, 否則被移除掉.
    if (context == AFHTTPRequestSerializerObserverContext) {
        if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
            [self.mutableObservedChangedKeyPaths removeObject:keyPath];
        } else {
            [self.mutableObservedChangedKeyPaths addObject:keyPath];
        }
    }
}

到現(xiàn)在, 我們就知道了 self.mutableObservedChangedKeyPaths其實(shí)就是我們自己設(shè)置的 request 屬性值得集合.
接下來(lái)[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];的方法,利用 KVO 把屬性值傳到 request.

  • (3) 把需要傳遞的參數(shù)進(jìn)行編碼, 并且設(shè)置到 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 進(jìn)行編碼, 并添加到 request 中
    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];
    // 來(lái)把各種類型的參數(shù), array dic set 轉(zhuǎn)化成字符串, 給 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 {
            // 默認(rèn)解析方式
            switch (self.queryStringSerializationStyle) {
                case AFHTTPRequestQueryStringDefaultStyle:
                    query = AFQueryStringFromParameters(parameters);
                    break;
            }
        }
    }
    // 最后判斷該 request 中是否包含 GET, HEAD, DELETE (都包含在 HTTPMethodsEncodingParametersInURI), 因?yàn)檫@幾個(gè) 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 請(qǐng)求

        // #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"];
        }
        // 設(shè)置請(qǐng)求體
        [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
    }
    return mutableRequest;
}

這個(gè)方法做了三件事情.

  • self.HTTPRequestHeaders拿到設(shè)置參數(shù), 賦值要請(qǐng)求的 request 里面去
  • 把請(qǐng)求的網(wǎng)絡(luò)的參數(shù), 從 array dic set 這些容器類型轉(zhuǎn)換為字符串, 具體轉(zhuǎn)碼方式, 我們可以使用自定義的方式, 也可以用 AF 默認(rèn)的轉(zhuǎn)碼方式. 自定義的轉(zhuǎn)碼方式根據(jù)自己想怎么解析來(lái)決定. 我們可以看看默認(rèn)的方式:
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
    NSMutableArray *mutablePairs = [NSMutableArray array];
    // 把參數(shù)給 AFQueryStringPairsFromDictionary, 拿到 AF 的一個(gè)類型的數(shù)據(jù)就是一個(gè) key, value 對(duì)象, 在URLEncodedStringValue 拼接 keyValue, 一個(gè)加在數(shù)組里
    for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
        [mutablePairs addObject:[pair URLEncodedStringValue]];
    }
    // 拆分?jǐn)?shù)組返回參數(shù)字符串
    return [mutablePairs componentsJoinedByString:@"&"];
}

NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
    // 往下調(diào)用
    return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}

NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
    NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
    // 根據(jù)需要排列的對(duì)象的 description 來(lái)進(jìn)行升序排列, 并且 selector 使用的是 compare.
    // 因?yàn)閷?duì)象的description返回的是NSString,所以此處compare:使用的是NSString的compare函數(shù)
    // 即@[@"foo", @"bar", @"bae"] ----> @[@"bae", @"bar",@"foo"]
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
    //判斷vaLue是什么類型的,然后去遞歸調(diào)用自己,直到解析的是除了array dic set以外的元素,然后把得到的參數(shù)數(shù)組返回。
    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;

轉(zhuǎn)碼主要是以上三個(gè)函數(shù), 配合著注釋理解, 主要是在遞歸調(diào)用AFQueryStringPairsFromKeyAndValue. 判斷vaLue 是什么類型, 然后去遞歸調(diào)用自己, 直到解析的是除了 array dic set 以外的元素, 然后把得到的參數(shù)數(shù)組返回.
其中有一個(gè)AFQueryStringPair對(duì)象, 其只有倆個(gè)屬性和倆個(gè)方法:

@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

方法很簡(jiǎn)單, 現(xiàn)在我們也開始理解這個(gè)轉(zhuǎn)碼過(guò)程了, 我們舉個(gè)例子梳理下, 就是下面的這三步:

@{ 
 @"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

到現(xiàn)在, 我們?cè)瓉?lái)容器類型的參數(shù), 就變成字符串類型了.
然后接著這個(gè)方法,和根據(jù)該 request 的請(qǐng)求類型,

 // 最后判斷該 request 中是否包含 GET, HEAD, DELETE (都包含在 HTTPMethodsEncodingParametersInURI), 因?yàn)檫@幾個(gè) 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 請(qǐng)求

        // #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"];
        }
        // 設(shè)置請(qǐng)求體
        [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
    }
    return mutableRequest;
}

到這里我們就生成了一個(gè) request.


看了這些, 我們?cè)倩氐?AFHTTPSessionManager 類中來(lái), 回到這個(gè)方法.

- (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;
    //把參數(shù),還有各種東西轉(zhuǎn)化為一個(gè)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"
            //如果解析錯(cuò)誤,直接返回
            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;
}

我們繼續(xù)往下看, 當(dāng)解析錯(cuò)誤的時(shí)候, 我們直接調(diào)用傳進(jìn)來(lái)的 fauler 的 Block 失敗返回了. 這里有一個(gè)self.completionQueue這個(gè)是我們自定義的, 這個(gè)是一個(gè)GCD的Queue如果設(shè)置了,那么從這個(gè)Queue中回調(diào)結(jié)果,否則從主隊(duì)列中回調(diào).
然后調(diào)用了父類生成task的方法, 并且自信了一個(gè)成功和失敗的回調(diào), 我們接著去父類的 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;
    //第一件事,創(chuàng)建NSURLSessionDataTask,里面適配了Ios8以下taskIdentifiers,函數(shù)創(chuàng)建task對(duì)象。
    //其實(shí)現(xiàn)應(yīng)該是因?yàn)閕OS 8.0以下版本中會(huì)并發(fā)地創(chuàng)建多個(gè)task對(duì)象,而同步有沒(méi)有做好,導(dǎo)致taskIdentifiers 不唯一…這邊做了一個(gè)串行處理
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
    return dataTask;
}

三、總結(jié)

在這實(shí)現(xiàn)過(guò)程中他還幫我們做了開線程,異步下載,檢查數(shù)據(jù)是否合法,圖片解壓等一些繁瑣的事情,所以我們才能用得這么舒心,但是我們還是有必要研究一下內(nèi)部的實(shí)現(xiàn),只有保持著一顆求知的心,我們才能研究的更深,得到的更多.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容