學(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)路徑filePath贪染,AFN會(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庫(kù)的擴(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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市腻惠,隨后出現(xiàn)的幾起案子环肘,更是在濱河造成了極大的恐慌,老刑警劉巖集灌,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悔雹,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡欣喧,警方通過查閱死者的電腦和手機(jī)腌零,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來唆阿,“玉大人益涧,你說我怎么就攤上這事】嵫唬” “怎么了饰躲?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵牙咏,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我嘹裂,道長(zhǎng)妄壶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任寄狼,我火速辦了婚禮丁寄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘泊愧。我一直安慰自己伊磺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布删咱。 她就那樣靜靜地躺著屑埋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪痰滋。 梳的紋絲不亂的頭發(fā)上摘能,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音敲街,去河邊找鬼团搞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛多艇,可吹牛的內(nèi)容都是我干的逻恐。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼峻黍,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼复隆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起奸披,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤昏名,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后阵面,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體轻局,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年样刷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了仑扑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡置鼻,死狀恐怖镇饮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情箕母,我是刑警寧澤储藐,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布俱济,位于F島的核電站,受9級(jí)特大地震影響钙勃,放射性物質(zhì)發(fā)生泄漏蛛碌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一辖源、第九天 我趴在偏房一處隱蔽的房頂上張望蔚携。 院中可真熱鬧,春花似錦克饶、人聲如沸酝蜒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亡脑。三九已至,卻和暖如春洲尊,著一層夾襖步出監(jiān)牢的瞬間远豺,已是汗流浹背奈偏。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工坞嘀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人惊来。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓丽涩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親裁蚁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子矢渊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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