1.網(wǎng)絡(luò)涉及到的基本概念
①客戶端 : 移動應(yīng)用/桌面應(yīng)用
②服務(wù)端 : 為客戶端提供服務(wù),提供數(shù)據(jù)和資源的機器。
③請求,響應(yīng),數(shù)據(jù)庫服務(wù)器等
服務(wù)器按開發(fā)階段來劃分:
1)遠程服務(wù)器(外網(wǎng)服務(wù)器/正式服務(wù)器)
應(yīng)用上線后使用的服務(wù)器,供全體用戶使用,速度取決于服務(wù)器性能和用戶網(wǎng)速 。2)本地服務(wù)器(內(nèi)網(wǎng)服務(wù)器/測試服務(wù)器)
應(yīng)用處于開發(fā)測試階段使用的服務(wù)器,供開發(fā)人員和測試人員使用,屬于局域網(wǎng),速度快,開發(fā)效率高。
2.如何找到服務(wù)器獲取所需資源
途徑: URL(Uniform Resoure Locator:統(tǒng)一資源定位器)
基本URL: 協(xié)議、服務(wù)器名稱(或IP地址)、路徑和文件名,如“協(xié)議://IP地址/路徑”
注意:不同的協(xié)議,代表著不同的資源查找方式,資源傳輸方式
基本通信過程:客戶端發(fā)請求給服務(wù)器,服務(wù)器接收到請求之后響應(yīng)
常見協(xié)議如下:
①http協(xié)議
概念:超文本傳輸協(xié)議,訪問的是遠程的網(wǎng)絡(luò)資源,是網(wǎng)絡(luò)開發(fā)中最常用的協(xié)議
格式:http://
優(yōu)點: 1)簡單快速(協(xié)議簡單,服務(wù)器端程序規(guī)模小,通信速度快) 2)靈活(允許傳輸各種數(shù)據(jù))
注意:1.1之前版本是非持續(xù)連接的
②file協(xié)議
概念:訪問的是本地計算機上的資源 (可以省略主機地址)
格式:file://
③mailto協(xié)議
概念:訪問的是電子郵件的地址
格式:mailto:
④FTP協(xié)議
概念:訪問的是共享主機上的資源
格式:ftp://
注意:
- 我們使用的網(wǎng)絡(luò)是在TCP/IP協(xié)議簇上運作的,而Http屬于他內(nèi)部的一個子集
- TCP協(xié)議簇中最重要的一點就是分層設(shè)計(OSI規(guī)定7層,實際只有4層)

這里我主要講Http協(xié)議(請求網(wǎng)絡(luò)資源)
1.Http通信大致可以分為兩大步驟:
請求:客戶端向服務(wù)器索要數(shù)據(jù)
響應(yīng):服務(wù)器返回客戶端相應(yīng)的數(shù)據(jù)
注意:
①發(fā)送請求的時候把請求頭和請求體(請求體是非必須的)包裝成一個請求對象
②服務(wù)器端對請求進行響應(yīng),在響應(yīng)信息中包含響應(yīng)頭和響應(yīng)體,響應(yīng)信息是對服務(wù)器端的描述,
具體的信息放在響應(yīng)體中傳遞給客戶端
③網(wǎng)絡(luò)請求返回的常見狀態(tài)碼
【200】:請求成功
【400】:客戶端請求的語法錯誤,服務(wù)器無法解析
【404】:無法找到資源,客戶端原因
【500】:服務(wù)器內(nèi)部錯誤,無法完成請求
小結(jié):狀態(tài)碼中4開頭的是客戶端原因;5開頭的是服務(wù)器原因
④發(fā)送請求的URL如果包含中文,發(fā)送請求前需要進行轉(zhuǎn)碼處理(注意瀏覽器的URL內(nèi)部已經(jīng)做了轉(zhuǎn)碼處理)
iOS中如何對URL進行轉(zhuǎn)碼:stringByAddingPercentEscapesUsingEncoding (NSUTF8StringEncoding)
2 Http請求方法: get, post, put, head, delete, options, trace,connect,patch, copy, lock, mkcol, move等
常見GET和POST請求的對比 :
- GET請求參數(shù)直接跟在URL后面
- POST請求的參數(shù)放在請求體中
建議:【除簡單數(shù)據(jù)查詢使用GET外,其它的一律使用POST請求】
3.iOS中發(fā)送HTTP請求的方案
1)蘋果原生
①NSURLConnection 03年推出的古老技術(shù)
②NSURLSession 13年推出iOS7之后,以取代NSURLConnection
③CFNetwork 底層技術(shù)、C語言的
2)第三方框架
①ASIHttpRequest (可惜已經(jīng)停止更新)
②AFNetworking (主流)
③MKNetworkKit
4.iOS 中發(fā)送http網(wǎng)絡(luò)請求方案實戰(zhàn)(原生)
NSURLConnection
使用步驟:
①確定好請求方式 :GET/POST(前者創(chuàng)建的是不可變請求對象,后者創(chuàng)建可變請求對象)
②確定好同步請求還是異步請求(開發(fā)中常見為異步請求)
1.NSURLConnection發(fā)送GET請求
①發(fā)送同步請求
-(void)sendSyncNetwork{
//1 創(chuàng)建不可變請求對象
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520&type=JSON"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//2 使用NSURLConnection發(fā)送同步請求
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response error:&error];
//3 解析服務(wù)器返回的數(shù)據(jù)
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);}
②發(fā)送異步請求(2種,block和代理)
1)block
-(void)sendAsyncNetwork{
//1 創(chuàng)建請求對象
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//2發(fā)送異步請求
/* 第一個參數(shù):請求對象
第二個參數(shù):操作隊列->(線程) 決定completionHandler回調(diào)在哪個線程中處理(主隊列-主線程)
第三個參數(shù):completionHandler回調(diào) 完成之后的回調(diào)
response:響應(yīng)頭信息
data:響應(yīng)體信息
connectionError:錯誤信息
*/
[NSURLConnection sendAsynchronousRequest: request queue[[NSOperationQueue alloc]init]
completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
//3 解析服務(wù)器返回的數(shù)據(jù)
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}];
}
2)代理
-(void)sendAsyncNetworkDelegate
{
//1 創(chuàng)建請求對象
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//2 設(shè)置代理 發(fā)送網(wǎng)絡(luò)請求
//NSURLConnection *connect = [[NSURLConnection alloc]initWithRequest:request delegate:self];
//設(shè)置代理的第二種方法 startImmediately是否要馬上發(fā)送請求
NSURLConnection *connect = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
//發(fā)送請求
[connect start];//此方式僅僅當(dāng)選擇的是不立即發(fā)送請求時,當(dāng)后面需要發(fā)送請求時才需要使用。
}
#pragma mark NSURLConnectionDataDelegate
//01 接受到響應(yīng)(一次)
-(void)connection:(NSURLConnection *)connection didReceiveResponse: (NSURLResponse *)response{
NSLog(@"didReceiveResponse");
}
//02 接收到服務(wù)器返回的數(shù)據(jù)(多次)
-(void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data{
NSLog(@"didReceiveData--%zd",data.length);//最終數(shù)據(jù)長度
[self.resultData appendData:data];
}
//03 失敗
-(void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error{
NSLog(@"didFailWithError");
}
//04 完成之后
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSLog(@"connectionDidFinishLoading");
//解析服務(wù)器返回的數(shù)據(jù)
NSLog(@"%@",[[NSString alloc]initWithData:self.resultData encoding:NSUTF8StringEncoding
]);
}
2.NSURLConnection發(fā)送POST請求 (這里只討論主流:異步 + block )
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//1創(chuàng)建可變請求對象(因為默認發(fā)送GET)
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; //+ 修改請求方法為POST
request.HTTPMethod = @"POST";
//+ 設(shè)置參數(shù)(請求體)
//username=520it&pwd=520it&type=JSON
request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
//2 發(fā)送異步請求
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
//3 解析服務(wù)器返回的數(shù)據(jù)
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]); }];
}
3.NSURLConneection與Runloop補充
①設(shè)置代理的方法中,如果立即發(fā)送請求,那么內(nèi)部會自動將connect作為一個source添加到runloop中
②設(shè)置代理的方法中,如果設(shè)置為NO不立即發(fā)送請求,那么內(nèi)部不會添加connect到runloop中,需調(diào)用start方法才會添加,調(diào)用start那么start內(nèi)部會把當(dāng)前的connect添加到runloop并且設(shè)置模式為默認,子線程中運行循環(huán)不存在,那么會主動創(chuàng)建。
注意:如果在子線程中發(fā)送網(wǎng)絡(luò)請求,則子線程的runloop需要手動創(chuàng)建并開啟,才能處理任務(wù)。
NSURLSession
1.使用步驟:
①創(chuàng)建NSURLSession對象(單例對象 或 自定義Session對象【調(diào)用類方法設(shè)置代理】)
②創(chuàng)建task (NSURLSessiontask是個抽象類,必須使用它的子類)
②執(zhí)行task (resume)

2.開發(fā)實戰(zhàn)
方案一: 通過Block 回調(diào)響應(yīng)體
-(void)getData
{
//1.創(chuàng)建會話對象
NSURLSession *session = [NSURLSession sharedSession];
//2.根據(jù)會話對象創(chuàng)建Task(請求任務(wù))
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//靈活多變,可變請求對象則可以為POST
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//注意:創(chuàng)建任務(wù)時還可以dataTaskWithURL ,則請求方式只能是GET
//解析服務(wù)器返回的數(shù)據(jù)
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
NSLog(@"%@",[NSThread currentThread]);//默認completionHandler在子線程中執(zhí)行
}];
//3.執(zhí)行Task(發(fā)送請求)
[dataTask resume];
}
注意: 簡便方式如下:
-(void)getData{
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/loginusername=520it&pwd=520it&type=JSON"]
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//解析數(shù)據(jù)
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}] resume];
}
方案二:通過代理獲得響應(yīng)體
-(void)delaget{
//1.創(chuàng)建會話對象 設(shè)置代理
/*
第三個參數(shù):隊列->線程(代理方法在哪個線程中執(zhí)行)|如果該參數(shù)傳遞nil 那么在子線程中執(zhí)行(默認)
*/
NSURLSession *session = [NSURLSession sessionWithConfiguration:
[NSURLSessionConfiguration defaultSessionConfiguration] delegate: self
delegateQueue:nil];
//2.創(chuàng)建請求任務(wù) (這里舉例POST請求)
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
//3.執(zhí)行Task
[dataTask resume];
}
#pragma mark NSURLSessionDataDelegate
//01 接收到服務(wù)器響應(yīng)的時候調(diào)用
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
NSLog(@"didReceiveResponse---%@",[NSThread currentThread]);//子線程
self.resultData = [NSMutableData data]; //tips
//需要告訴系統(tǒng)是否接收服務(wù)器返回的數(shù)據(jù)
completionHandler(NSURLSessionResponseAllow
);
}
//02 接受到服務(wù)器返回數(shù)據(jù)的時候調(diào)用 (可能被調(diào)用多次)
-(void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data{
//拼接服務(wù)器返回的數(shù)據(jù)
[self.resultData appendData:data];
}
//03 請求完成或者是失敗的時候調(diào)用
-(void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error{
//解析服務(wù)器返回的數(shù)據(jù)
NSLog(@"%@",[[NSString alloc]initWithData:self.resultData encoding:NSUTF8StringEncoding]);
}
3.實戰(zhàn)總結(jié)
①創(chuàng)建Task的方法中如果需要傳入“請求對象”,那么請求方式的方式可以是GET/POST,具體取決于你。反之方法參數(shù)直接傳URL,那么請求方式只能是GET(因為沒有單獨的請求體)
②如果是想利用block發(fā)送請求Task,則利用sharedSession創(chuàng)建單例會話對象;反之利用代理方法,則自定義會話對象
5.數(shù)據(jù)解析
1.須知: 服務(wù)器返回給客戶端的數(shù)據(jù)一般都是JSON格式或者XML格式(文件下載除外)
2.數(shù)據(jù)格式介紹:
①JSON
1)JSON是一種輕量級的數(shù)據(jù)格式,一般用于數(shù)據(jù)交互
2)JSON的格式很像OC中的字典和數(shù)組
{"name" : "jack", "age" : 10}
{"names" : ["jack", "rose", "jim”]}
3)標(biāo)準(zhǔn)JSON格式的注意點:key必須用雙引號
4)JSON 與OC 對照表
JSON OC
大括號 { } NSDictionary
中括號 [ ] NSArray
雙引號 " " NSString
數(shù)字 10、10.8 NSNumber
在iOS中,JSON的常見解析方案有4種:
第三方框架:JSONKit、SBJson、TouchJSON(性能從左到右,越差)
蘋果原生(自帶):NSJSONSerialization(性能最好)
NSJSONSerialization的常見方法:
-
JSON數(shù)據(jù) -> OC對象 (反序列化,得到OC的字典或數(shù)組 => 模型 故常用)
+(id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error; -
OC對象 ? -> JSON數(shù)據(jù) (序列化)
+(NSData *)dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError **)error;
②XML
1)全稱是Extensible Markup Language,譯作“可擴展標(biāo)記語言”。跟JSON一樣,也是常用的一種用于交互的數(shù)據(jù)格式
一般也叫XML文檔(XML Document)
- 一個常見的XML文檔一般由以下三部分組成
- 文檔聲明
- 元素(Element)
- 屬性(Attribute
XML語法 – 文檔聲明
在XML文檔的最前面,必須編寫一個文檔聲明,用來聲明XML文檔的類型
最簡單的聲明:
<?xml version="1.0" ?>
用encoding屬性說明文檔的字符編碼
<?xml version="1.0" encoding="UTF-8" ?>
XML語法 – 元素(Element)
①一個元素包括了開始標(biāo)簽和結(jié)束標(biāo)簽
擁有內(nèi)容的元素: <video>哈哈</video>
沒有內(nèi)容的元素: <video></video>
沒有內(nèi)容的元素簡寫: <video/>
②一個元素可以嵌套若干個子元素(不能出現(xiàn)交叉嵌套)
<videos>
<video>
<name>小黃人 第01部</name>
<length>30</length>
</video>
</videos>
③規(guī)范的XML文檔最多只有1個根元素,其他元素都是根元素的子孫元素
XML語法 –元素的注意: XML中的所有空格和換行,都會當(dāng)做具體內(nèi)容處理
故下面兩個元素的內(nèi)容是不一樣的
第1個
<video>小黃人</video>
第2個
<video>
小黃人
</video>
XML語法 – 屬性(Attribute)
①一個元素可以擁有多個屬性
<video name="小黃人 第01部" length="30" />
video元素擁有name和length兩個屬性
②屬性值必須用 雙引號"" 或者 單引號'' 括住
③實際上,屬性表示的信息也可以用子元素來表示,比如
<video>
<name>小黃人 第01部</name>
<length>30</length>
</video>
XML解析
1.須知:XML的解析方式有2種
- DOM:一次性將整個XML文檔加載進內(nèi)存,比較適合解析小文件
- SAX:從根元素開始,按順序一個元素一個元素往下解析,比較適合解析大文件
2.iOS中的XML解析方式:
①蘋果原生
NSXMLParser:SAX方式解析,使用簡單,文件大小通吃
②第三方框架
libxml2:純C語言,默認包含在iOS SDK中,同時支持DOM和SAX方式解析
GDataXML:DOM方式解析,由Google開發(fā),基于libxml2
3.XML解析方式的選擇建議
大文件:NSXMLParser、libxml2
小文件:GDataXML、NSXMLParser、libxml2
注意:相比之下,JSON的體積小于XML,所以服務(wù)器返回給移動端的數(shù)據(jù)格式以JSON居多
6.數(shù)據(jù)解析實戰(zhàn)
1.復(fù)雜JSON解析
1.解析步驟:
①確定服務(wù)器返回給客戶端的數(shù)據(jù)是數(shù)組還是字典亦或是其他:直接百度json在線格式化或者把JSON數(shù)據(jù)寫plist文件再觀察
②. 一般反序列化成OC對象(字典或數(shù)組)再面向模型開發(fā)
2.經(jīng)驗總結(jié):
①反序列化(JSON -> OC對象)方法中,options參數(shù)不能亂填 (=> 確定響應(yīng)體類型很重要?。。。?br>
NSJSONReadingMutableContainers = (1UL << 0), 是可變的
NSJSONReadingMutableLeaves = (1UL << 1), 得到的字典中所有的string都是可變的
NSJSONReadingAllowFragments = (1UL << 2) 當(dāng)最外層既不是字典也不是數(shù)組的時候必須使用該枚舉
id obj = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
注意:kNilOptions 表示0 默認的意思
②并不是所有的對象都可以序列化
1)最外層的對象必須是 NSArray or NSDictionary里面所有的對象都只能是 NSString, NSNumber, NSArray, NSDictionary, or NSNull
2)所有字典的keys are NSStrings
3)NSNumbers 必須是合法并且不能是無窮大
故 保險起見,序列化之前會進行判斷一下是否可以序列化
if(![NSJSONSerialization isValidJSONObject:array])
{
NSLog(@"該對象不支持序列化處理");
return;
}
③json文件中的數(shù)據(jù)為NSData,不能通過字典獲取
//NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:@"test.json" ofType:nil]];
NSData *jsonData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:@"test.json" ofType:nil]];
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:nil];
④在將JSON數(shù)據(jù)寫入文件的時候,一般會對JSON數(shù)據(jù)進行排版
//JSON -->OC對象 反序列化處理
id obj = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSLog(@"%@--%@",[obj class],obj);
//對json數(shù)據(jù)進行排版
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:obj options:NSJSONWritingPrettyPrinted error:nil];
//寫json數(shù)據(jù)
[jsonData writeToFile:@"/Users/wuyuanping/Desktop/test.json" atomically:YES];
⑤面向模型開發(fā)會存在的問題【容易出錯|重命名|關(guān)鍵字等】
服務(wù)器返回的數(shù)據(jù)很多,我們只要一部分 => 可能并不能直接使用KVC
服務(wù)器提供的屬性名字是關(guān)鍵字 => 需要給屬性重新命名
屬性過多轉(zhuǎn)換費時且無技術(shù)含量 => 一般會用框架字典轉(zhuǎn)模型(或者運行時)
拓展:
1.字典轉(zhuǎn)模型框架:
- Mantle:需要繼承自MTModel
- JSONModel:需要繼承自JSONModel
- MJExtension:不需要繼承,無代碼侵入性
- YYModel :高性能
2.選擇和使用框架的注意點:
①侵入性
②易用性,是否容易上手
③可擴展性,很容易給這個框架增加新的功能
MJExtension使用舉例:
//1.把字典數(shù)組轉(zhuǎn)換為模型數(shù)組
self.videos = [YPVideo objectArrayWithKeyValuesArray:videoArray];
//2.重命名模型屬性的名稱
//第一種重命名屬性名稱的方法,有一定的代碼侵入性)
//設(shè)置字典中的id被模型中的ID替換
+(NSDictionary *)replacedKeyFromPropertyName
{
return @{
@"ID":@"id"
};
}
//第二種重命名屬性名稱的方法,代碼侵入性為零
[YPVideo setupReplacedKeyFromPropertyName:^NSDictionary *{
return @{
@"ID":@"id"
};
}];
//3.MJExtension框架內(nèi)部實現(xiàn)原理-運行時
代碼示例
1.JSON解析
- (void)viewDidLoad{
[super viewDidLoad];
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/video?type=JSON"]
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//輸出結(jié)果表明:返回的是字典(元素為存放字典的數(shù)組)
// NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
//解析服務(wù)器返回的數(shù)據(jù) JSON-->OC對象(反序列化)
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
//把字典數(shù)組->模型數(shù)組
NSArray *arrayM = dict[@"videos"];
NSMutableArray *arrM = [NSMutableArray array];
for (NSDictionary *dict in arrayM) {
[arrM addObject:[YPVideo videoWithDict:dict]];
}
self.videos = arrM;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//刷新UI,得跳到主線程中
[self.tableView reloadData];
}];
}] resume];
}
2.XML解析
解析步驟
①創(chuàng)建解析器
②設(shè)置代理,遵守代理協(xié)議,實現(xiàn)代理方法,在代理方法中解析元素
③開始解析
- (void)viewDidLoad{
[super viewDidLoad];
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/video?type=XML"]
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//01 創(chuàng)建XML解析器
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
//02 設(shè)置代理
parser.delegate = self;
//03 開始解析 該方法本身是阻塞
[parser parse]; //跳到他的代理方法
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//刷新UI
[self.tableView reloadData];
}];
}] resume];
}
#pragma mark NSXMLParserDelegate
//01 開始解析XML文檔
-(void)parserDidStartDocument:(NSXMLParser *)parser{
NSLog(@"parserDidStartDocument");
}
//02 開始解析文檔中某個元素的時候調(diào)用 該方法可能會被調(diào)用多次
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict{
//過濾掉根元素,否則報錯
if ([elementName isEqualToString:@"videos"]) { return; }
NSLog(@"didStartElement--%@--%@",elementName,attributeDict);
//檢驗參數(shù)代表什么
//每當(dāng)?shù)玫揭粋€字典 就應(yīng)該轉(zhuǎn)換為模型 并添加到全局的可變數(shù)組
[self.videos addObject:[YPVideo mj_objectWithKeyValues:attributeDict]];
}
//03 某個元素解析完畢的時候調(diào)用
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName{
NSLog(@"didEndElement");
}
//04 整個XML文檔解析完畢
-(void)parserDidEndDocument:(NSXMLParser *)parser{
NSLog(@"parserDidEndDocument");
}
使用GDataXML解析XML 步驟:
前提:
- 配置環(huán)境 :
① 先導(dǎo)入框架 #import "GDataXMLNode.h"
② 配置環(huán)境(GDataXML框架是MRC的,所以還需要告訴編譯器以MRC的方式處理GDataXML的代碼) - 步驟:
//1.加載XML文檔(使用的是DOM的方式一口氣把整個XML文檔都吞下)
GDataXMLDocument *doc = [[GDataXMLDocument alloc]initWithData:data options:kNilOptions error:nil];
//2.獲取XML文檔的根元素,根據(jù)根元素取出XML中的每個子元素
NSArray * elements = [doc.rootElement elementsForName:@"video"];
//3. 取出每個子元素的屬性并轉(zhuǎn)換為模型
for (GDataXMLElement *ele in elements) {
YPVideo *video = [[YPVideo alloc]init];
video.name = [ele attributeForName:@"name"].stringValue;
video.length = [eleattributeForName:@"length"].stringValue.integerValue;
video.url = [ele attributeForName:@"url"].stringValue;
video.image = [ele attributeForName:@"image"].stringValue;
video.ID = [ele attributeForName:@"id"].stringValue;
//4. 把轉(zhuǎn)換好的模型添加到tableView的數(shù)據(jù)源self.videos數(shù)組中
[self.videos addObject:video];
}
案例 - NSURLSession實現(xiàn)大文件下載
問題解決思路:
①文件下載內(nèi)存飆升 => 直接將數(shù)據(jù)寫入沙盒 (文件句柄 或者 輸出流)
②斷點下載 => 設(shè)置請求頭 (Range)
③離線斷點下載 => 代理方法(可以實時將下載數(shù)據(jù)存入沙盒)
注意:輸出流創(chuàng)建文件路徑是懶加載,故不論調(diào)用幾次,只會創(chuàng)建一個文件夾,
對比使用文件句柄指針得要手動控制只創(chuàng)建一個文件夾,輕松不少。