一、前言
最近有些時(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ò)展工具包.
其中 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),只有保持著一顆求知的心,我們才能研究的更深,得到的更多.