網(wǎng)絡(luò)(詳解)

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層)
Snip20170208_4.png

這里我主要講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)

Snip20170208_5.png

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)

  1. 一個常見的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)建一個文件夾,輕松不少。

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

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

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