AFNetworking是我們常用的網(wǎng)絡(luò)庫(kù)狈醉,我們有必要對(duì)其有必要的了解瞻鹏,以便進(jìn)行二次封裝和遇到問(wèn)題時(shí)能及時(shí)的進(jìn)行調(diào)試开仰。而AF本身是對(duì)系統(tǒng)的NSURLSession進(jìn)行的封裝脐往,所以想了解AF的整體架構(gòu),有必要先了解一下OC對(duì)于網(wǎng)絡(luò)請(qǐng)求的基本姿勢(shì)谒获。
一個(gè)簡(jiǎn)單的網(wǎng)絡(luò)請(qǐng)求
NSURL *url = [NSURL URLWithString:@"http://www.apple.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *holdSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionDataTask * dataTask = [holdSession dataTaskWithRequest:request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) {
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
NSLog(@"******%@\n%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding],res.allHeaderFields);
if (error) {
NSLog(@"******error:%@",[error localizedDescription]);
}
}];
[dataTask resume];
構(gòu)建一個(gè)網(wǎng)絡(luò)請(qǐng)求:
第一步:先準(zhǔn)備一個(gè)URL組建成NSRequest
第二步:根據(jù)配置信息NSURLSessionConfiguration構(gòu)建一個(gè)會(huì)話NSURLSession
第三步:給NSURLSession分配一個(gè)數(shù)據(jù)請(qǐng)求的任務(wù)dataTaskWithRequest
第四步:異步發(fā)送請(qǐng)求蛤肌,處理回調(diào)的數(shù)據(jù)。
看來(lái)我們用系統(tǒng)API發(fā)送一個(gè)網(wǎng)絡(luò)請(qǐng)求還是很方便的芭裸准!但是構(gòu)建一個(gè)穩(wěn)定高效的網(wǎng)絡(luò)系統(tǒng),我們還是有很多坑要填的赔硫,我們就來(lái)看下AFNetworking吧炒俱。
AFNetworking的基本架構(gòu)
右側(cè)為非核心模塊:
Security模塊:為安全模塊,處理https鏈接中的SSL相關(guān)配置卦停。
UIKit模塊:為一些系統(tǒng)類的拓展和輔助功能向胡,像圖片的緩存恼蓬,相關(guān)網(wǎng)絡(luò)控件網(wǎng)絡(luò)事件的監(jiān)控惊完。
Reachability是AF自己封裝的監(jiān)測(cè)網(wǎng)絡(luò)狀態(tài)的模塊。
左側(cè)為網(wǎng)絡(luò)請(qǐng)相關(guān)核心模塊
AFHTTPRequestSerializer本事是一個(gè)協(xié)議处硬,就一個(gè)方法小槐。這個(gè)方法傳入兩個(gè)參數(shù):NSURLRequest和URL參數(shù)字典,返回一個(gè)NSURLRequest荷辕。目的是實(shí)現(xiàn)對(duì)應(yīng)前面栗子中NSURLRequest進(jìn)行封裝凿跳,主要是對(duì)于請(qǐng)求頭和各種請(qǐng)求形態(tài)的編碼預(yù)處理,達(dá)到http請(qǐng)求的要求疮方。下面是對(duì)其包含的相關(guān)類進(jìn)行一下說(shuō)明控嗜。
主要有三種請(qǐng)求格式:
大部分的請(qǐng)求都是AFHTTPRequestSerializer這個(gè)來(lái)完成的也是默認(rèn)的配置,另外還有AFJSONRequestSerializer和AFPropertyListRequestSerializer兩種請(qǐng)求類型骡显,這兩者都是繼承與AFHTTPRequestSerializer疆栏,主要在于前者用于特別的處理請(qǐng)求content-type是json文件的時(shí)候曾掂,后者用于特別的處理請(qǐng)求content-type是plist文件的時(shí)候。AFQueryStringPair實(shí)現(xiàn)對(duì)網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)進(jìn)行編碼并組建成鍵值對(duì)用=連接,編碼相關(guān)問(wèn)題可以來(lái)這里看壁顶。主要是內(nèi)聯(lián)函數(shù)AFPercentEscapedStringFromString實(shí)現(xiàn)了相關(guān)設(shè)置珠洗。
AFStreamingMultipartFormData主要用于文件上傳時(shí)的一些設(shè)置。它其中有一個(gè)主要屬性就是AFMultipartBodyStream若专,其繼承于NSInputStream许蓖,是對(duì)讀取上傳資源文件流的一些設(shè)置。
AFHTTPBodyPart主要是post或put請(qǐng)求中的請(qǐng)求體相關(guān)的設(shè)置
AFURLResponseSerialization類本身也是一個(gè)協(xié)議调衰,該方法傳入一個(gè)response和一個(gè)data返回指定類型的對(duì)象膊爪。這個(gè)協(xié)議用于出來(lái)網(wǎng)絡(luò)請(qǐng)求返回的數(shù)據(jù)response,對(duì)數(shù)據(jù)進(jìn)行驗(yàn)證嚎莉、解碼蚁飒,并根據(jù)MIME類型返回正確的數(shù)據(jù)。
AFHTTPResponseSerializer下面其他幾個(gè)類型的基類萝喘,用于驗(yàn)證返回的數(shù)據(jù)是否正確淮逻,確認(rèn)解碼方式,code碼等阁簸。
AFJSONResponseSerializer處理application/json爬早、text/json、text/javascript數(shù)據(jù)启妹,我們最常用的出具處理方式筛严,返回序列化后的id對(duì)象。
AFXMLParserResponseSerializer和AFXMLDocumentResponseSerializer處理application/xml饶米、text/xml數(shù)據(jù)桨啃。前者生成NSXMLParser對(duì)象,后者生成NSXMLDocument對(duì)象檬输。
AFPropertyListResponseSerializer處理application/x-plist數(shù)據(jù)
AFImageResponseSerializer處理image這個(gè)主類型下所有類型
AFCompoundResponseSerializer應(yīng)對(duì)多種處理類型照瘾,自行判斷可以處理的類型。
AFHTTPSessionManager繼承與AFURLSessionManager丧慈,針對(duì)HTTP請(qǐng)求的各種請(qǐng)求類型進(jìn)行了封裝析命,主要目的是對(duì)外提供便于使用的接口,這里實(shí)現(xiàn)了栗子中逃默,NSURLRequest的準(zhǔn)備鹃愤,拿到對(duì)應(yīng)的task,并對(duì)task進(jìn)行啟動(dòng)完域。
AFURLSessionManager核心類软吐,實(shí)現(xiàn)對(duì)task請(qǐng)求的hook,準(zhǔn)備session吟税,監(jiān)控session的各種代理狀態(tài)凹耙,監(jiān)控task的各種代理狀態(tài)鸟蟹,并利用AFURLResponseSerialization進(jìn)行數(shù)據(jù)的解析與回調(diào)。
NSURLSessionConfiguration是比較重要的配置信息類使兔,三個(gè)初始化方法:
- +defaultSessionConfiguration返回標(biāo)準(zhǔn)配置建钥,共享NSHTTPCookieStorage,共享NSURLCache和共享NSURLCredentialStorage虐沥;
- +ephemeralSessionConfiguration返回一個(gè)預(yù)設(shè)配置熊经,沒(méi)有持久性存儲(chǔ)的緩存,Cookie或證書欲险;+
- +backgroundSessionConfiguration:它會(huì)創(chuàng)建一個(gè)后臺(tái)session镐依,它以在應(yīng)用程序掛起,退出天试,甚至崩潰的情況下運(yùn)行上傳和下載任務(wù)槐壳。
_AFURLSessionTaskSwizzling實(shí)現(xiàn)對(duì)resume、suspend的hook喜每,并發(fā)送通知务唐。
AFURLSessionManagerTaskDelegate實(shí)現(xiàn)對(duì)task的上傳、下載進(jìn)度的回調(diào)带兜,獲取數(shù)據(jù)枫笛,獲取到完整數(shù)據(jù)后進(jìn)行回調(diào)。
AFNetworking的線程相關(guān)問(wèn)題
網(wǎng)絡(luò)的重點(diǎn)在于數(shù)據(jù)的高并發(fā)處理刚照,所以有必要研究一下AF在線程方面所做的努力刑巧。
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
AF創(chuàng)建session的時(shí)候會(huì)建一個(gè)隊(duì)列傳進(jìn)去,這個(gè)隊(duì)列默認(rèn)設(shè)置最大并發(fā)數(shù)只有1,第一次這個(gè)session啟動(dòng)一個(gè)任務(wù)的時(shí)候啟動(dòng)一個(gè)線程來(lái)處理這個(gè)網(wǎng)絡(luò)請(qǐng)求任務(wù)无畔,以后這個(gè)會(huì)話下的任務(wù)默認(rèn)都是在這個(gè)線程中順序執(zhí)行得啊楚。
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[delegate setupProgressForTask:task];
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
session每次啟動(dòng)一個(gè)任務(wù),就會(huì)創(chuàng)建一個(gè)AFURLSessionManagerTaskDelegate的delegate對(duì)象浑彰,用于處理數(shù)據(jù)恭理。AFHTTPSessionManager有一個(gè)字典屬性mutableTaskDelegatesKeyedByTaskIdentifier用于存放以taskID為鍵,delegate對(duì)象為值得一組數(shù)據(jù)闸昨,這個(gè)delegate對(duì)象就是AFURLSessionManagerTaskDelegate生成的對(duì)象蚯斯。此處為應(yīng)對(duì)可能在不同的線程生成任務(wù),添加了鎖饵较,防止線程競(jìng)爭(zhēng)造成的數(shù)據(jù)異常。
dispatch_async(url_session_manager_processing_queue(), ^{
NSError *serializationError = nil;
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
.......
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, serializationError);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
});
此處代碼是數(shù)據(jù)回來(lái)之后遭赂,對(duì)數(shù)據(jù)的處理循诉。由于要處理的數(shù)據(jù)可能很大,所以首先開啟一個(gè)異步并發(fā)隊(duì)列url_session_manager_processing_queue撇他,在子線程中處理數(shù)據(jù)茄猫,處理完成后再回調(diào)到應(yīng)該去的線程狈蚤。通過(guò)異步group,判斷是否用戶創(chuàng)建了group划纽,如果沒(méi)有就創(chuàng)建一個(gè)脆侮,同時(shí)判斷用戶是否創(chuàng)建了完成的隊(duì)列completionQueue,如果沒(méi)有就返回到主線程勇劣。理我可以監(jiān)聽自定義group靖避,可以實(shí)現(xiàn)多個(gè)請(qǐng)求完畢后的統(tǒng)一處;通過(guò)設(shè)置自定義completionQueue比默,可以讓我們的數(shù)據(jù)返回到自定義線程幻捏,一個(gè)場(chǎng)景就是返回的數(shù)據(jù)可能要進(jìn)行大量的計(jì)算,這個(gè)時(shí)候我們沒(méi)必要返回主線程命咐,直接去自定義的子線程做這些復(fù)雜計(jì)算篡九,待完成后再回到主線程去刷新UI。最后還有一個(gè)點(diǎn)就是為了保證通知在主線程醋奠,特地做了異步返回主線程去發(fā)通知榛臼。
總結(jié):
AFNetworking面向接口編程,整體架構(gòu)清爽窜司,每個(gè)點(diǎn)拓展開來(lái)都有不少內(nèi)容讽坏,值得我們學(xué)習(xí),我只是對(duì)架構(gòu)進(jìn)行了整體的分析例证,希望對(duì)大家有所啟發(fā)路呜。閑來(lái)無(wú)事大家可以就感興趣的一些模塊仔細(xì)閱讀,定會(huì)對(duì)整個(gè)網(wǎng)絡(luò)請(qǐng)求以及線程的管理有更多的理解织咧。