NSURLConnection資料解析

weixin_33890499發表於2016-04-18

在網路開發中,從伺服器獲取的二進位制資料包括:

> html

> 圖片

> 視訊

> 音訊

> zip 等

除了以上檔案格式之外,最常見的資料莫過於JSON,偶爾也會有XML。

無論 JSON還是 XML都是一種特殊格式的字串,按照特定的規則描述資料結構。

一、準備工作--->安裝Apache伺服器

> 參考備課筆記/筆記/02-Apache伺服器安裝筆記.m

二、JSON

1、JSON格式介紹

# JSON本質上,就是一個“特殊格式”的字串。

> JSON是網路上用來傳輸資料使用最廣泛的資料格式,沒有之一。

> JSON出生草根,是Javascript的子集,是Javascript的SON,專門用來描述資料結構。

> Javascrpit 是做網頁開發使用的一種"指令碼"語言

> Javascrpit & Java沒有任何關係! 就好像 雷鋒 和 雷峰塔 之間的關係。

#參考網站:http//www.w3cschool.cc。w3c 是國際網際網路組織的縮寫。

2、JSON的語法規則

> 資料以 key/value 鍵值對錶示

> 資料由逗號分割

> 花括號儲存物件,對應OC的NSDictionary。

> 方括號儲存陣列,對應OC的NSArray。

3> JSON值

* 數字(整數或浮點數)

* 字串(在雙引號中)

* 邏輯值(true 或 false)

* 陣列(在方括號中)

* null

4、JSON解析--->解析天氣預報

> 序列化:在向伺服器傳送資料之前,將 NSArray/NSDcitionary 轉成二進位制的過程。

> 反序列化:在從伺服器接收到資料之後,將二進位制資料轉換成NSArray/NSDcitionary的過程。

/**

*  解析天氣預報

*/

- (void)loadData{

// 建立 url

NSURL *url = [NSURL URLWithString:@"http://www.weather.com.cn/adat/sk/101010100.html"];

// 建立請求物件

NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:10.0];

// 傳送非同步請求

[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {

NSLog(@"data = %@",data);

/*

> 序列化:在向伺服器傳送資料之前,將 NSArray/NSDcitionary 轉成二進位制的過程。

> 反序列化:在從伺服器接收到資料之後,將二進位制資料轉換成NSArray/NSDcitionary的過程。

*/

NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);

/*

NSJSONReadingMutableContainers = (1UL << 0), 容器可變

NSJSONReadingMutableLeaves = (1UL << 1), 葉子可變

NSJSONReadingAllowFragments(碎片) = (1UL << 2) 頂級節點可以既不是陣列也不是字典

*/

NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];

NSLog(@"%@市 溫度 %@ 風向 %@ 風力 %@ ",dict[@"weatherinfo"][@"city"],dict[@"weatherinfo"][@"temp"],dict[@"weatherinfo"][@"WD"],dict[@"weatherinfo"][@"WS"]);

}];

}

5、JSON解析第三方框架

> 常見的JSON解析第三方框架

* JSONKit(最快)

* SBJson

* TouchJSON

# 以上三個框架的效能依次降低。

> 介紹JSONKit第三方框架的目的

* JSON的解析並不是表面上這麼簡單

* 官方說JSONKit 比蘋果原生的 JSON 解析速度要快。

* JSONKit在很多老的專案中仍然在使用

#知道JSONKit 說明我們是資深的iOS程式設計師。

* JSONKit已經在2012年停止更新,適用於iOS5.0之前的版本開發使用。(蘋果原生的NSJSONSerialization是iOS5.0之後出現)

* 瞭解 ARC & MRC 混編的方法

> JSONKit解析和效能測試

使用步驟:

* 下載框架:http://github.com/johnezang/JSONKit

* 匯入框架檔案:JSONKit.h & JSONKit.m

* 設定 MRC 標記

> 選擇"專案"-"Build Phases"-"Compile Sources"

> 找到 JSONKit.m 並且在 Compiler Flags 中新增 -fno-objc-arc。

# -fno-objc-arc 告訴編譯器,編譯 JSONKit.m 時不使用ARC。

* 修改錯誤

> 利用自動修復功能,修改兩處isa的錯誤。

* 反序列化

> id  result =  [[JSONDecoder decoder] objectWithData:data];

使用程式碼和測試如下:

int largeNumber = 100 * 1000;

- (void)loadData{

// 建立 url

NSURL *url = [NSURL URLWithString:@"http://localhost/demo.json"];

// 建立請求物件

NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:10.0];

// 傳送非同步請求

[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {

/*

官方說JSONKit 比蘋果原生的 JSON 解析速度要快。我們可以通過測試驗證下

JSONKit:4.907761 秒

官方:0.299994 秒

通過測試得出:蘋果原生的JSON解析速度遠遠比JSONKit快。

如果工作中遇到使用第三方框架解析 JSON 的舊專案,建議替換為蘋果原生的。

替換的原則:看專案是否要支援 iOS5.0之前的版本。如果不需要果斷替換。

替換步驟:

1> 備份(給自己一個後悔的機會)

2> 刪除 JSONKit.h & .m 檔案

3> 哪裡出錯改哪裡

*/

CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();

for (int index = 0; index < largeNumber; index ++) {

//            NSMutableDictionary *result =  [[JSONDecoder decoder] objectWithData:data];

NSMutableDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];

}

NSLog(@"-----endTime = %f",CFAbsoluteTimeGetCurrent() - start);

//JKDictionary 其實就是 NSMutableDictionary 的 子類

//        NSLog(@"------%@,%@",result,result[@"message"]);

}];

}

6、PList解析

> PList 主要在蘋果開發中常用,很多後臺並不會返回 Plist 的格式資料。有關 PList的反序列化知道即可。

> 程式碼如下:

/**

*  解析plist 資料

*/

- (void)loadData{

// 建立 url

NSURL *url = [NSURL URLWithString:@"http://localhost/videos.plist"];

// 建立請求物件

NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:10.0];

// 傳送非同步請求

[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {

/**

引數:

- data:要反序列化的二進位制資料

- options:選項,位移列舉型別

NSPropertyListImmutable = 0  不可變,

NSPropertyListMutableContainers = 1  容器可變,

NSPropertyListMutableContainersAndLeaves = 2  容器和葉子可變

- format:如果不希望知道格式,傳人 NULL 即可

- error:錯誤資訊

*/

id result = [NSPropertyListSerialization propertyListWithData:data options:0 format:NULL error:nil];

NSLog(@"%@  %@",result,[result class]);

}];

}

三、XML解析

1、XML介紹

> XML的英文全稱(eXtensible Markup Language),中文名稱可擴充套件標記語言。

> XML的特點:語法規則很簡單,且很有邏輯。

#出身名門,w3c制定,微軟和 IBM 曾經共同大力推薦過的資料格式。

> 專門設計用來傳輸和儲存資料

> 與HTML的區別:HTML的標記不是所有的都需要成對出現,

XML要求所有的標記必須成對出現。

HTML標記不區分大小寫,它則大小敏感,即區分大小寫。

2、XML解析的方式

> DOM解析

* 是在MAC使用的解析方式。

* 記憶體消耗極大,不適用於手機。因此蘋果沒有提供手機上DOM解析。

* iPhone無法直接使用DOM方式解析XML。

> SAX解析

* 是隻讀的方式,從上向下的方式解析

* 是蘋果提供的解析方式

* 使用 NSXMLParser 通過 代理 實現解析。

3、SAX解析步驟

1> 開始文件--準備工作

2> 開始"節點"

3> 發現節點內部的內容,每一個節點,可能需要多次才能找完

4> 結束"節點"

5> 結束文件--解析結束

#以上步驟,2,3,4會不斷迴圈,一直到所有解析完成。

4、程式碼演示XML的解析。

/**

*  載入 xml 資料

*/

- (void)loadData{

// 建立 url

NSURL *url = [NSURL URLWithString:@"http://localhost/videos.xml"];

// 建立請求物件

NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:10.0];

// 傳送非同步請求(由於解析 xml 是一個耗時的過程,所以新開啟佇列)

[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {

// 1.建立 xml 解析器

NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];

// 2.設定代理來解析

parser.delegate = self;

// 3.解析器開始解析 -- 一旦開始解析,後續的解析工作全部交給代理完成

[parser parse];

}];

}

> 通過NSLog列印,確認XML解析思路

#pragma mark  - NSXMLParserDelegate 代理方法

/**

*  開始解析

*/

- (void)parserDidStartDocument:(NSXMLParser *)parser {

NSLog(@"1、開始解析");

}

/**

*  解析到一個開始節點呼叫

*/

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

NSLog(@"2、開始節點 %@ %@",elementName,attributeDict);

}

/**

*  解析到節點文字呼叫  一個節點的內容可以會呼叫多次該方法

*/

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {

NSLog(@"3、======> %@",string);

}

/**

*  解析到結束節點呼叫  elementName 沒有反斜線 /

*/

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {

NSLog(@"4、結束節點------%@",elementName);

}

/**

*  解析結束呼叫

*/

- (void)parserDidEndDocument:(NSXMLParser *)parser{

NSLog(@"5、結束文件");

}

5、畫XML解析思維導圖

6、XML解析程式碼實現

#pragma mark  - NSXMLParserDelegate 代理方法

/**

*  開始解析

*/

- (void)parserDidStartDocument:(NSXMLParser *)parser {

// 清空陣列

[self.videos removeAllObjects];

}

/**

*  解析到一個開始節點呼叫

*/

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

if ([elementName isEqualToString:@"video"]) {

// 建立 video 模型

self.currentVideo = [[Video alloc] init];

// 設定 id

self.currentVideo.videoId = @([attributeDict[@"videoId"] integerValue]);

}

// 清空當前正在拼接的字元內容

[self.elementString setString:@""];

// 錯誤寫法

//self.elementString = @"";

}

/**

*  解析到節點文字呼叫  一個節點的內容可以會呼叫多次該方法

*/

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {

[self.elementString appendString:string];

}

/**

*  解析到結束節點呼叫  elementName 沒有反斜線 /

*/

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {

// 如果 elementName 是 video,則新增到陣列中

/* 最笨方法

if ([elementName isEqualToString:@"video"]){

[self.videos addObject:self.currentVideo];

} else if ([elementName isEqualToString:@"name"]){

self.currentVideo.name = self.elementString;

} else if ([elementName isEqualToString:@"length"]){

self.currentVideo.length = @(self.elementString.integerValue);

} else if ([elementName isEqualToString:@"desc"]){

self.currentVideo.desc = self.elementString;

} else if ([elementName isEqualToString:@"imageURL"]) {

self.currentVideo.imageURL = self.elementString;

} else if ([elementName isEqualToString:@"videoURL"]){

self.currentVideo.videoURL = self.elementString;

} else if ([elementName isEqualToString:@"teacher"]){

self.currentVideo.teacher = self.elementString;

}

*/

// 如果是 name,length,desc....則設定屬性

// 簡便方法

if ([elementName isEqualToString:@"video"]){

[self.videos addObject:self.currentVideo];

} else if(![elementName isEqualToString:@"videos"]){

//使用 KVC 設定屬性, KVC是一種以字串形式間接設定資料的方式

[self.currentVideo setValue:self.elementString forKey:elementName];

}

}

/**

*  解析結束呼叫

*/

- (void)parserDidEndDocument:(NSXMLParser *)parser{

NSLog(@"%@",self.videos);

}

四、tableView展示資料

1、展示視訊資料--Storyboard自定義cell

2、設定時間格式

> HMVideo模型中提供屬性

//視訊時長

@property (nonatomic, strong,readonly) NSString *time;

# 計算時間方式有兩種

# 方式一:重寫time屬性getter方法

- (NSString *)time {

int len = self.length.intValue;

return [NSString stringWithFormat:@"%02d:%02d:%02d", len / 3600, (len % 3600) / 60, (len % 60)];

}

# 方式二:

- (void)setLength:(NSNumber *)length {

_length = length;

int len = self.length.intValue;

_time = [NSString stringWithFormat:@"%02d:%02d:%02d", len / 3600, (len % 3600) / 60, (len % 60)];

}

3、SDWebImage下載頭像

UIImage *placeholder = [UIImage imageNamed:@"user_default"];

// SDWebImageLowPriority:會在表格滾動的時候,暫時影象下載,效能會好很多

// SDWebImageRetryFailed:圖片下載失敗後重試

[self.iconView sd_setImageWithURL:video.fullImageUrl placeholderImage:placeholder options:SDWebImageRetryFailed | SDWebImageLowPriority];

4、整合下列重新整理控制元件-->通過程式碼和storyboard

/**

*  整合下拉重新整理控制元件(通過程式碼)

*/

- (void)setRefreshControl{

// 整合下拉控制元件(不需要設定frame)

self.refreshControl = [[UIRefreshControl alloc] init];

// 監聽下拉事件

[self.refreshControl addTarget:self action:@selector(loadData) forControlEvents:UIControlEventValueChanged];

// 設定提示文字,顏色

NSMutableAttributedString *attributedTitle = [[NSMutableAttributedString alloc] initWithString:@"努力載入中……" attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:15],NSForegroundColorAttributeName:[UIColor redColor]}];

self.refreshControl.attributedTitle = attributedTitle;

// 設定菊花顏色

self.refreshControl.tintColor = [UIColor grayColor];

}

# UIRefreshControl使用說明

1> 目前只對UITableviewController有用

2> 只能下拉重新整理,不能上拉重新整理

3> init或者viewdidload中建立UIRefreshControl,設定文字,顏色等資訊

4> 系統自動管理UIRefreshControl,自動新增到tableview檢視中;

5> 給UIRefreshControl新增方法,當值改變的時候呼叫,方法用於資料請求

6> 該方法中請求資料確認完成之後,呼叫endRefreshing方法,關閉重新整理

五、XML解析重構

1> 新建一個模型類HMSAXVideo,專門做解析工作。

2> 提供類方法:+(void)saxParser:(NSData *)data finished:(void (^)(NSArray *))finished。

// 如果回撥的 block 在當前方法不執行,需要用一個屬性記錄

+(void)saxParser:(NSData *)data finished:(void (^)(NSArray *))finished{

// 使用斷言確保外界傳人的回撥不為空,如果為空,則崩潰提示

NSAssert(finished != nil, @"必須傳人回撥");

// 0.建立解析模型

HMSAXVideo *sax = [[HMSAXVideo alloc] init];

// 記錄回撥 block

sax.finishedBlock = finished;

// 1.建立 xml 解析器

NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];

// 2.設定代理來解析

parser.delegate = sax;

// 3.解析器開始解析

// parse:是同步方法,會等所用代理方法執行完畢後,才會執行後面的程式碼

[parser parse];

#warning 不能在parse方法呼叫後記錄回撥

// 記錄回撥 block

// sax.finishedBlock = finished;

}

六、Copy,NSNumber,id

1、為什麼模型HMVideo中的屬性都使用copy? 改為strong執行看看效果。

> strong:在設定數值的時候,僅僅是引用計算+1

> copy:在設定數值的時候,如果有方法是可變的,會copy一個不可變的副本。copy動作是在 setter方法中執行的。

> 畫圖解析使用strong導致的效果。

> 如果屬性使用strong:

#下面的程式碼

[self.currentVideo setValue:self.elementString forKey:elementName];

#修改為

[self.currentVideo setValue:self.elementString.copy forKey:elementName];

> 重新了屬性的setter方法,就是接管了原有系統提供的setter方法。

# 如果重新了copy屬性的 setter 方法,一定要 copy,否則外面的copy屬性就是strong屬性。

比如重寫了模型的name屬性的 setter方法:

- (void)setName:(NSString *)name {

_name = [name copy];

//    _name = name; 屬性就相當於 strong 了

}

2、NSNumber,id屬性

> 程式碼演示實際開發中不用NSNumber帶來的問題。

> 程式碼演示伺服器返回資料中有id鍵值對的問題。

相關文章