AFURLSessionManager 這個類是AFN框架的核心類,基本上通過它來實現(xiàn)了大部分核心功能。負責(zé)請求的建立、管理、銷毀、安全、請求重定向、請求重啟等各種功能。他主要實現(xiàn)了NSURLSession和NSRULSessionTask的封裝。
首先來對比下系統(tǒng)URLSession網(wǎng)絡(luò)請求與AFN網(wǎng)絡(luò)請求的方法
系統(tǒng)提供的URLSession網(wǎng)絡(luò)POST請求方法使用
NSURL *securl = [NSURL URLWithString:authenticate_appAuthenticate];
NSMutableURLRequest *secrequest = [[NSMutableURLRequest alloc]initWithURL:securl
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:1000];
[secrequest setValue:[MHXIMPAFOper hashRule:[MHXIMPAFOper getTimeStamp]
uuid:[MHUniqueID uniqueCFUUID]]
forHTTPHeaderField:@"X-IMPF"];
[secrequest setHTTPMethod:@"POST"];
SendObject *secsend = [[SendObject alloc]init];
NSString *secbodyStr = [[secsend getSend] yy_modelToJSONString];
NSData *secbody = [secbodyStr dataUsingEncoding:NSUTF8StringEncoding];
[secrequest setHTTPBody:secbody];
NSURLSession *requestSessions = [NSURLSession sharedSession];
NSURLSessionDataTask *requestTasks = [requestSessions dataTaskWithRequest:secrequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"data ======= %@, response ======= %@, error ======= %@",data ,response, error);
}];
[requestTasks resume];
AFN中POST方法使用(這里進行了一層簡單的封裝)
+(AFHTTPSessionManager *)httpManager{
// 獲取請求對象
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 設(shè)置請求格式
manager.requestSerializer = [AFJSONRequestSerializer serializer];
// 設(shè)置返回格式
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/plain", @"multipart/form-data", @"application/json", @"text/html", @"image/jpeg", @"image/png", @"application/octet-stream", @"text/json", nil];
[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
manager.requestSerializer.timeoutInterval = kRequestTimeOut;
return manager;
}
+(__kindof NSURLSessionTask *)Post:(NSString *)URLString parameters:(id)parameters success:(void(^)(id responseObject))success failure:(void(^)(NSError *))failure{
AFHTTPSessionManager *manager = [self httpManager];
return [manager POST:URLString parameters:parameters progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
if (success) {
success(responseObject);
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (failure) {
failure(error);
}
}];
}
通過對比可以發(fā)現(xiàn),AFN幫我們將繁瑣的網(wǎng)絡(luò)請求進行了封裝,只需要傳入訪問服務(wù)器URL以及參數(shù),就可以通過block返回給對應(yīng)的正確或者錯誤數(shù)據(jù),而且還可以通過progress的block不斷獲取進度。
AFHTTPSessionManager中一共提供了四種初始化方法,但最終都會指向調(diào)用- (instancetype)initWithBaseURL:(NSURL *)url sessionConfiguration:(NSURLSessionConfiguration *)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;
}
初始化時,調(diào)用了父類的方法[super initWithSessionConfiguration:configuration]
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
//設(shè)置默認的configuration,配置session
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
//當前實例變量持有configuration
self.sessionConfiguration = configuration;
//設(shè)置delegate的操作隊列并發(fā)線程數(shù)量為1,即串行隊列
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
/*
*/
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
//默認為JSON解析
self.responseSerializer = [AFJSONResponseSerializer serializer];
//設(shè)置默認證書,無條件信任HTTPS認證
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
//網(wǎng)絡(luò)狀態(tài)監(jiān)聽
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
//存放一個 delegate = value,taskId = key 的字典
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
//使用NSLock確保線程安全
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
//異步獲取當前session所有未完成的task。會讓后臺任務(wù)重新回來初始化session,之前可能存在的任務(wù)進行置空處理
//參考AFN問題匯總https://github.com/AFNetworking/AFNetworking/issues/3499
[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;
}
從上述代碼中可以看出,在AFN初始化過程中,除了對NSURLSession進行初始化之外,還設(shè)置了許多默認配置,例如創(chuàng)建串行隊列、默認JSON解析、無條件信任證書、保證線程安全、網(wǎng)絡(luò)狀態(tài)監(jiān)聽以及舊任務(wù)置空處理等操作。
以POST請求為例,調(diào)用的
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString parameters:(nullable id)parameters progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure方法
進入該方法查看實現(xiàn),發(fā)現(xiàn)除了常用的POST和GET請求之外,還有PUT、HEAD、DELETE、PATCH這些其它請求類型的方法實現(xiàn),都執(zhí)行了同一個方法
- (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
在該方法中,首先調(diào)用AFHTTPRequestSerializer的requestWithMethod函數(shù)構(gòu)建request;當有錯誤時,處理request構(gòu)建產(chǎn)生的錯誤 - serializationError;其中relativeToURL表示將URLString拼接至baseURL中。接下來,繼續(xù)查看requestSerializer的創(chuàng)建方法實現(xiàn)。

其中最重要的部分,就是將request的各種屬性進行遍歷,用于給mutableRequest自帶的屬性賦值。
關(guān)于AFHTTPRequestSerializerObservedKeyPaths(),查看實現(xiàn)方法可以發(fā)現(xiàn)返回一個帶有方法名的數(shù)組,定義了一個static的方法,表示該方法只能在本文件中使用。利用runtime的反射調(diào)用NSStringFromSelector方法,將六個方法名轉(zhuǎn)換成字符串存入數(shù)組中。而這個六個方法又分別對應(yīng)了mutableRequest的六個屬性名稱。

從上往下,依次代表:
1. 是否允許使用設(shè)備的蜂窩移動網(wǎng)絡(luò)來創(chuàng)建request,默認為允許;2.創(chuàng)建的request所使用的緩存策略,默認使用NSURLRequestUseProtocolCachePolicy,該策略表示如果緩存不存在,直接從服務(wù)端獲取。如果緩存存在,會根據(jù)response中的Cache-Control字段判斷下一步操作,如: Cache-Control字段為must-revalidata, 則 詢問服務(wù)端該數(shù)據(jù)是否有更新,無更新直接返回給用戶緩存數(shù)據(jù),若已更新,則請求服務(wù)端.3. 如果設(shè)置HTTPShouldHandleCookies為YES,就處理存儲在NSHTTPCookieStore中的cookies HTTPShouldHandleCookies表示是否應(yīng)該給request設(shè)置cookie并隨request一起發(fā)送出去4.HTTPShouldUsePipelining表示receiver(理解為iOS客戶端)的下一個信息是否必須等到上一個請求回復(fù)才能發(fā)送。如果為YES表示可以,NO表示必須等receiver收到先前的回復(fù)才能發(fā)送下個信息5.設(shè)定request的network service類型. 默認NSURLNetworkServiceTypeDefault. 這個network service是為了告訴系統(tǒng)網(wǎng)絡(luò)層這個request使用的目的。 比如NSURLNetworkServiceTypeVoIP表示的這個request是用來請求網(wǎng)際協(xié)議通話技術(shù)(Voice over IP)。系統(tǒng)能根據(jù)提供的信息來優(yōu)化網(wǎng)絡(luò)處理,從而優(yōu)化電池壽命,網(wǎng)絡(luò)性能等等,客戶端基本不使用6.超時機制,默認60秒至于要在此處實現(xiàn)遍歷的目的,是因為在AFHTTPRequestSerializer類的init方法中,AFN給上面指定的6個方法選擇器每一個元素添加了KVO觀察者


當數(shù)組的元素發(fā)生變化時,判斷新值是否為空。若為空則從mutableObservedChangedKeyPaths可變集合中移除;不為空,則添加至可變集合中。
小提示:關(guān)于NSNull,包含了唯一方法+(NSNull *)null,是一個對象,用于表示零值的單獨對象。主要用于不能使用nil的場景下,例如可變數(shù)組中,想插入一個空對象的情況。
因此,在AFHTTPSessionManager初始完成之后,需要額外添加自定義的request配置時,比如超時時間設(shè)置為10秒。這時KVO監(jiān)聽到timeoutInterval的屬性發(fā)生變化,將keyPath添加到mutableObservedChangedKeyPaths可變集合中。然后,在執(zhí)行網(wǎng)絡(luò)請求方法時,會遍歷該可變集合,通過KVC動態(tài)的給mutableRequest添加value,最終實現(xiàn)將自定義配置添加至request中。
而且在AFHTTPRequestSerializer類的初始化方法中,AFN自動添加了網(wǎng)絡(luò)請求頭部內(nèi)容

接下來,到了AFN的重點,對傳入的參數(shù)字典進行處理。
使用AFN傳入的參數(shù)格式為字典,但在網(wǎng)絡(luò)請求中,是要轉(zhuǎn)換成key=value&key=value的形式(GET請求直接拼接到URL之后,POST請求放入request body中),才能傳給服務(wù)端獲取有效的數(shù)據(jù)。

NSString * AFQueryStringFromParameters(NSDictionary *parameters)C函數(shù)的作用,是遍歷數(shù)組中的AFQueryStringPair,然后以&符號拼接。AFQueryStringPair是一個數(shù)據(jù)處理類,只有兩個屬性:field和value;一個方法:-URLEncodedStringValue。它的作用就是上面我們說的,以key=value的形式,然后用URL Encode編碼,拼接成字符串。在遍歷過程中,最終指向了
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value)函數(shù),為了保證參數(shù)字典中,所有的value類型確保為字符串類型,這里便使用了遞歸,針對value可能為字典、數(shù)組、集合的類型時進行解析。例如,之前的項目中當需要向后臺傳輸一段用戶的定位數(shù)據(jù)用于分析行動軌跡時,傳參的字典中的location的key值嵌套了一個包含分別以經(jīng)度和緯度為key的字典。
當value為字典、數(shù)組、集合的類型時,會執(zhí)行遞歸解析,直到value類型都不為上述類型時,向mutableQueryStringComponents添加一個AFQueryStringPair類的對象,其中傳入key和value并返回出去。
(這里AFN框架執(zhí)行了升序排列,這里不是很明白為什么要先將字典的key進行升序排列再進行數(shù)據(jù)遞歸解析)
當傳參字典中所有數(shù)據(jù)解析完成之后,會通過遍歷返回為AFQueryStringPair類的對象,然后將該對象進行百分號編碼,用于處理可能存在包含歧義或者不符合規(guī)劃的字符(可以自行查找關(guān)于百分號編碼相關(guān)資料),最后將字符串拼接"&"符號。
以上,就是AFN框架中,發(fā)起網(wǎng)絡(luò)請求之前,關(guān)于request處理相關(guān)的操作全部流程分析,其中最主要的功能就是對傳參參數(shù)進行了數(shù)據(jù)的遞歸解析,其次對request的六個相關(guān)屬性進行KVO監(jiān)聽,可以在初始化AFHTTPSessionManager對象之后,自定義修改指定request屬性時,通過KVO代理方法以及動態(tài)KVC最終在request中實現(xiàn)配置修改。
該文章首次發(fā)表在 簡書:我只不過是出來寫寫代碼 博客,并自動同步至 騰訊云:我只不過是出來寫寫iOS 博客