學(xué)習(xí)AFN

說實(shí)話,AFN是個(gè)博大精深的東西,作為一款全世界都在使用的iOS框架想要一下子就參透也是不大可能的,但是,我還是要契而不舍的鉆研他,參透他!

1.AFN的使用

要研究一款框架的實(shí)現(xiàn),首先要了解這個(gè)框架的基本使用,在開發(fā)中我們用到AFN主要就是在發(fā)送網(wǎng)絡(luò)請(qǐng)求、下載數(shù)據(jù)、上傳數(shù)據(jù)。

用AFN可以非常方便的發(fā)送get請(qǐng)求和post請(qǐng)求,方法相同,只是需要修改一下參數(shù)和函數(shù)名。

AFHTTPSessionManager* manager = [AFHTTPSessionManager manager];

//我們也可以把這部分加粗的參數(shù)存放在一個(gè)字典里,然后通過parameters這個(gè)參數(shù)傳遞。

[manager GET:@"http://120.25.226.186:32812/login?username=123&pwd=123&type=JSON" parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) {子線程執(zhí)行

? ? } success:^(NSURLSessionDataTask * _Nonnull task, id? _Nullable responseObject) {

? ? ? ? //task.response是請(qǐng)求頭

? ? ? ? NSLog(@"%@",responseObject);

? ? ? ? NSLog(@"success");主線程執(zhí)行

? ? } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

? ? ? ? NSLog(@"failure");主線程執(zhí)行

? ? }];

post方法也是一樣的,只不過把請(qǐng)求的參數(shù)存放到一個(gè)字典里傳遞。

下載文件的方法:

-(void)download{

? ? AFHTTPSessionManager* manager = [AFHTTPSessionManager manager];

? ? NSURL* url = [[NSURL alloc] initWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];

? ? NSURLRequest* request = [[NSURLRequest alloc] initWithURL:url];

? ? NSURLSessionDownloadTask* task = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) { ? ? ? ? NSLog(@"%f",1.0*downloadProgress.completedUnitCount/downloadProgress.totalUnitCount);??:這里用downloadProgress自帶的兩個(gè)參數(shù)相除就可以得到下載進(jìn)度,在2.x版本的時(shí)候沒有這個(gè)回調(diào)參數(shù),需要使用kvo監(jiān)聽下載進(jìn)度的改變。

? ? } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {

??:這個(gè)block是有返回值的,需要返回一個(gè)NSURL,在這個(gè)block塊里我們需要把下載到的文件從臨時(shí)路徑剪切到指定的目標(biāo)路徑。targetPath就是AFN為我們自動(dòng)下載到的臨時(shí)路徑,我們需要提供一個(gè)目標(biāo)路徑filePathAFN會(huì)自動(dòng)把下載下來的文件從臨時(shí)文件夾剪切到filePath路徑下

? ? ? ? NSString* filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject]stringByAppendingPathComponent:response.suggestedFilename];

? ? ? ? NSLog(@"1-----%@",filePath);

? ? ? ? NSLog(@"2-----%@",[NSThread currentThread]);//這段代碼是在子線程執(zhí)行的

? ? ? ? return [NSURL fileURLWithPath:filePath];

? ? } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {

? ? ? ? NSLog(@"download finish");

? ? ? ? NSLog(@"2-----%@",filePath);

? ? ? ? NSLog(@"2-----%@",[NSThread currentThread]);主線程執(zhí)行

? ? }];

? ? [task resume];需要手動(dòng)開啟執(zhí)行任務(wù)

}

上傳文件的方法:

-(void)upload{

????????AFHTTPSessionManager* manager = [AFHTTPSessionManager manager]; ? ? ? ? ? ? ? ? ? ? ? ? ?

??:這里的上傳文件的方法不是什么什么upload,而是一種post方法

????????[manager POST:@"http://120.25.226.186:32812/upload" parameters:nil constructingBodyWithBlock:^(id _Nonnull formData) {

? ? ? ? [formData appendPartWithFileURL:[NSURL fileURLWithPath:@"/Users/apple/Documents/我自己 2.jpg"] name:@"my" error:nil];

? ? } progress:^(NSProgress * _Nonnull uploadProgress) { ? ?????NSLog(@"%f",1.0*uploadProgress.completedUnitCount/uploadProgress.totalUnitCount);//上傳的操作是開啟子線程并發(fā)執(zhí)行的

? ? } success:^(NSURLSessionDataTask * _Nonnull task, id? _Nullable responseObject) {

? ? ? ? NSLog(@"success");//下載成功的回調(diào)在主線程執(zhí)行

? ? } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

? ? ? ? NSLog(@"fail");//下載失敗的回調(diào)在主線程執(zhí)行

? ? }];

}

2.框架的結(jié)構(gòu)

AFNetworking主要分為五個(gè)模塊:

網(wǎng)絡(luò)通信模塊(AFURLSessionManager、AFHTTPSessionManger)

網(wǎng)絡(luò)狀態(tài)監(jiān)聽模塊(Reachability)

網(wǎng)絡(luò)通信安全策略模塊(Security)

網(wǎng)絡(luò)通信信息序列化/反序列化模塊(Serialization)

對(duì)于iOS UIKit庫的擴(kuò)展(UIKit)

3.對(duì)不同類型數(shù)據(jù)的解析

如果我們請(qǐng)求的數(shù)據(jù)是json類型的,那么正確返回的數(shù)據(jù)也是json類型的,這個(gè)時(shí)候我們不需要修改數(shù)據(jù)的解析方案,直接請(qǐng)求就可以了。

如果我們請(qǐng)求的數(shù)據(jù)是xml類型的,這個(gè)時(shí)候就需要修改數(shù)據(jù)的解析方案了。

-(void)getXML{

AFHTTPSessionManager* manager = [AFHTTPSessionManager manager];

??:設(shè)置解析方法:manager.responseSerializer = [AFXMLParserResponseSerializer serializer];

[manager GET:@"http://120.25.226.186:32812/login2?username=123&pwd=123&type=XML" parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) { ? ? ? ? ? ?`????????????NSLog(@"%f",1.0*downloadProgress.completedUnitCount/downloadProgress.totalUnitCount); }

success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { //這個(gè)時(shí)候的responseObject是NSXMLParser類型的,無法直接拿到數(shù)據(jù) NSXMLParser* parser = (NSXMLParser*)responseObject;把responseObject強(qiáng)制轉(zhuǎn)化為NSXMLParser類型的對(duì)象,之后用代理方法進(jìn)行解析。

parser.delegate = self;

[parser parse]; }

failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { NSLog(@"fail---%@",error); }];}

//用代理方法解析數(shù)據(jù)

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{

? ? NSLog(@"%@---%@",elementName,attributeDict);

}

如果請(qǐng)求到的數(shù)據(jù)既不是json類型的,也不是xml類型的,這個(gè)時(shí)候就需要使用這種解析方法:

manager.responseSerializer = [AFHTTPResponseSerializer serializer];

請(qǐng)求到的responseObject是NSData類型的,可以對(duì)他進(jìn)行下一步的轉(zhuǎn)化。其實(shí)這種方法是默認(rèn)的,可以解析所有類型的數(shù)據(jù)。

4.為什么要用AFN框架

說到為什么要用這個(gè)框架,我們就必須要知道在沒有使用這個(gè)框架之前用iOS原生的機(jī)制來處理網(wǎng)絡(luò)請(qǐng)求會(huì)發(fā)生什么問題。

首先我們來說最原始的NSURLConnection是怎么發(fā)送網(wǎng)絡(luò)請(qǐng)求的。

發(fā)送網(wǎng)絡(luò)請(qǐng)求的方法可以分為兩類,異步發(fā)送請(qǐng)求和同步發(fā)送請(qǐng)求。

異步發(fā)送網(wǎng)絡(luò)請(qǐng)求有兩種方法,一種是sendAsynchronousRequest,一種是用delegate。

同步發(fā)送網(wǎng)絡(luò)請(qǐng)求用sendSynchronousRequest。

如果是使用sendSynchronousRequest或者sendAsynchronousRequest方法,我們是用block來處理回調(diào)的,可以指定NSOperationQueue指定回調(diào)方法在哪個(gè)隊(duì)列執(zhí)行。

如果是在主線程用代理方法發(fā)送網(wǎng)絡(luò)請(qǐng)求,那么代理方法也是在主線程執(zhí)行的,如果要讓代理方法在子線程執(zhí)行可以開子線程發(fā)送網(wǎng)絡(luò)請(qǐng)求,或者設(shè)置setDelegateQueue。

那么只要是遇到了發(fā)送網(wǎng)絡(luò)請(qǐng)求的部分,我們就需要讓UIViewController去遵守協(xié)議,實(shí)現(xiàn)代理方法。這樣做的壞處就是,網(wǎng)絡(luò)層和控制器寫在了一塊兒,沒有剝離開,也沒有實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求的統(tǒng)一管理。

基于NSURLConnection的AFN做了什么呢?

就是把網(wǎng)絡(luò)請(qǐng)求和回調(diào)進(jìn)行了統(tǒng)一的管理,不需要為每一個(gè)請(qǐng)求開啟子線程,這里的做法是開啟一條常駐子線程處理所有的請(qǐng)求和響應(yīng)。

那么NSURLSession對(duì)比起NSURLConnection有哪些不同呢?

參考文章:從 NSURLConnection 到 NSURLSession

?首先我們討論一個(gè)問題,就是使用NSURLConnection容易造成什么問題?

NSURLConnectoin只隱藏了單個(gè)網(wǎng)絡(luò)請(qǐng)求的線程的相關(guān)操作,并沒有提供接口來解決多個(gè)網(wǎng)絡(luò)請(qǐng)求時(shí)多個(gè)線程的管理問題。

并且NSURLConnection不是基于HTTP/2協(xié)議的,若使用NSURLConnection發(fā)起請(qǐng)求則每次請(qǐng)求都需要經(jīng)過三次握手過程。而使用了session之后,使用同一個(gè)session中的task訪問數(shù)據(jù),不用每次都實(shí)現(xiàn)三次握手,復(fù)用之前的連接可以加快訪問速度。

5.源碼分析

這里有一篇非常特別的源碼分析,他不是在分析AFN這個(gè)源碼的實(shí)現(xiàn)流程,而是總結(jié)了自己在學(xué)習(xí)這個(gè)源碼的過程中學(xué)到了哪些思想。

AFNetworking 3.0 源碼解讀 總結(jié)(干貨)(上)

重要的方法1:AFURLSessionManager的initWithSessionConfiguration:方法

????self.sessionConfiguration = configuration;

? ? self.operationQueue = [[NSOperationQueue alloc] init];

? ? self.operationQueue.maxConcurrentOperationCount = 1;

? ? self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];??:這里設(shè)置的代理操作隊(duì)列的最大并發(fā)數(shù)為1,為了讓所有的請(qǐng)求的發(fā)起和等待網(wǎng)絡(luò)響應(yīng)都在同一個(gè)線程,不需要為每一個(gè)請(qǐng)求創(chuàng)建一個(gè)線程。

self.responseSerializer = [AFJSONResponseSerializer serializer]; self.securityPolicy = [AFSecurityPolicy defaultPolicy];

使用代理方法完成進(jìn)一步的操作。

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data

{

? ? AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];在這個(gè)方法里,給dataTask添加了一個(gè)代理,這個(gè)代理負(fù)責(zé)處理后續(xù)的數(shù)據(jù)拼接和數(shù)據(jù)操作。在實(shí)現(xiàn)delegateForTask:這個(gè)方法的時(shí)候,上鎖,并給delegate一個(gè)唯一的標(biāo)識(shí),防止不同的task使用同一個(gè)delegate。

? ? [delegate URLSession:session dataTask:dataTask didReceiveData:data];

? ? if (self.dataTaskDidReceiveData) {

? ? ? ? self.dataTaskDidReceiveData(session, dataTask, data);

? ? }

}

數(shù)據(jù)傳輸完成后,調(diào)用didCompleteWithError:(NSError *)error方法:

- (void)URLSession:(NSURLSession *)session ?task:(NSURLSessionTask *)task

didCompleteWithError:(NSError *)error

{

? ? AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];

? ? // delegate may be nil when completing a task in the background

? ? if (delegate) {

? ? ? ? [delegate URLSession:session task:task didCompleteWithError:error];

? ? ? ? [self removeDelegateForTask:task];把task對(duì)應(yīng)的代理從代理字典里移除

? ? }

? ? if (self.taskDidComplete) {

? ? ? ? self.taskDidComplete(session, task, error);

? ? }

}

??:AFN中使用了兩個(gè)操作隊(duì)列,一條是在AFURLSessionManager里的初始化方法里創(chuàng)建的最大并發(fā)數(shù)為1的NSOperationQueue,用來處理所有的網(wǎng)絡(luò)請(qǐng)求和等待響應(yīng)。數(shù)據(jù)的解析在一個(gè)異步并發(fā)的操作隊(duì)列里執(zhí)行。

同時(shí)多個(gè)網(wǎng)絡(luò)請(qǐng)求直接多次調(diào)用AFHTTPSessionManager的GET方法就行了。一個(gè)請(qǐng)求依賴另一個(gè)請(qǐng)求的結(jié)果,在第一個(gè)請(qǐng)求的成功或失敗回調(diào)中發(fā)起第二個(gè)請(qǐng)求就是最好的方法。

參考文章:AFNetworking源碼分析

AFN中的兩種代理:

有三個(gè)代理方法轉(zhuǎn)發(fā)到了AFN的代理中,這三個(gè)方法里的代理是需要對(duì)應(yīng)每個(gè)task做私有化處理的。

其他的方法都試針對(duì)這個(gè)sessionManager所有的request的,是公用的處理。

三個(gè)方法分別是:didCompleteWithError、didReceiveData、didFinishDownloadingToURL

6.AFN中圖片的解壓

首先我們要知道為什么要對(duì)圖片解壓,我們下載到的JPG,PNG類型的圖片是不能直接用來顯示的。當(dāng)我們調(diào)用UIImage的方法imageWithData:方法把數(shù)據(jù)轉(zhuǎn)成UIImage對(duì)象后,其實(shí)這時(shí)UIImage對(duì)象還沒準(zhǔn)備好需要渲染到屏幕的數(shù)據(jù),現(xiàn)在的網(wǎng)絡(luò)圖像PNG和JPG都是壓縮格式,需要把它們解壓轉(zhuǎn)成bitmap后才能渲染到屏幕上,如果不做任何處理,當(dāng)你把UIImage賦給UIImageView,在渲染之前底層會(huì)判斷到UIImage對(duì)象未解壓,沒有bitmap數(shù)據(jù),這時(shí)會(huì)在主線程對(duì)圖片進(jìn)行解壓操作,再渲染到屏幕上。這個(gè)解壓操作是比較耗時(shí)的,如果任由它在主線程做,可能會(huì)導(dǎo)致速度慢UI卡頓的問題。

7.網(wǎng)絡(luò)請(qǐng)求中的緩存

網(wǎng)絡(luò)請(qǐng)求的緩存和我們經(jīng)常用到的圖片的緩存,數(shù)據(jù)的緩存其實(shí)是一個(gè)木目的,就是為了加快請(qǐng)求的響應(yīng)時(shí)間,避免重復(fù)發(fā)送相同的請(qǐng)求,同時(shí)提升離線或低網(wǎng)速情況下的用戶體驗(yàn)。

當(dāng)一個(gè)請(qǐng)求完成下載來自服務(wù)器的回應(yīng),一個(gè)緩存的回應(yīng)將在本地保存。下一次同一個(gè)請(qǐng)求再發(fā)起時(shí),本地保存的回應(yīng)就會(huì)馬上返回,不需要連接服務(wù)器。

AFN中的緩存機(jī)制是通過NSCache來實(shí)現(xiàn)的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容