NSURLConnection數(shù)據(jù)解析

在網(wǎng)絡開發(fā)中,從服務器獲取的二進制數(shù)據(jù)包括:

> html

> 圖片

> 視頻

> 音頻

> zip 等

除了以上文件格式之外,最常見的數(shù)據(jù)莫過于JSON,偶爾也會有XML。

無論 JSON還是 XML都是一種特殊格式的字符串,按照特定的規(guī)則描述數(shù)據(jù)結(jié)構(gòu)。

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

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

二、JSON

1、JSON格式介紹

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

> JSON是網(wǎng)絡上用來傳輸數(shù)據(jù)使用最廣泛的數(shù)據(jù)格式,沒有之一。

> JSON出生草根,是Javascript的子集,是Javascript的SON,專門用來描述數(shù)據(jù)結(jié)構(gòu)。

> Javascrpit 是做網(wǎng)頁開發(fā)使用的一種"腳本"語言

> Javascrpit & Java沒有任何關(guān)系! 就好像 雷鋒 和 雷峰塔 之間的關(guān)系。

#參考網(wǎng)站:http//www.w3cschool.cc。w3c 是國際互聯(lián)網(wǎng)組織的縮寫。

2、JSON的語法規(guī)則

> 數(shù)據(jù)以 key/value 鍵值對表示

> 數(shù)據(jù)由逗號分割

> 花括號保存對象,對應OC的NSDictionary。

> 方括號保存數(shù)組,對應OC的NSArray。

3> JSON值

* 數(shù)字(整數(shù)或浮點數(shù))

* 字符串(在雙引號中)

* 邏輯值(true 或 false)

* 數(shù)組(在方括號中)

* null

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

> 序列化:在向服務器發(fā)送數(shù)據(jù)之前,將 NSArray/NSDcitionary 轉(zhuǎn)成二進制的過程。

> 反序列化:在從服務器接收到數(shù)據(jù)之后,將二進制數(shù)據(jù)轉(zhuǎn)換成NSArray/NSDcitionary的過程。

/**

*? 解析天氣預報

*/

- (void)loadData{

// 創(chuàng)建 url

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

// 創(chuàng)建請求對象

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

// 發(fā)送異步請求

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

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

/*

> 序列化:在向服務器發(fā)送數(shù)據(jù)之前,將 NSArray/NSDcitionary 轉(zhuǎn)成二進制的過程。

> 反序列化:在從服務器接收到數(shù)據(jù)之后,將二進制數(shù)據(jù)轉(zhuǎn)換成NSArray/NSDcitionary的過程。

*/

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

/*

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

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

NSJSONReadingAllowFragments(碎片) = (1UL << 2) 頂級節(jié)點可以既不是數(shù)組也不是字典

*/

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已經(jīng)在2012年停止更新,適用于iOS5.0之前的版本開發(fā)使用。(蘋果原生的NSJSONSerialization是iOS5.0之后出現(xiàn))

* 了解 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{

// 創(chuàng)建 url

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

// 創(chuàng)建請求對象

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

// 發(fā)送異步請求

[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 主要在蘋果開發(fā)中常用,很多后臺并不會返回 Plist 的格式數(shù)據(jù)。有關(guān) PList的反序列化知道即可。

> 代碼如下:

/**

*? 解析plist 數(shù)據(jù)

*/

- (void)loadData{

// 創(chuàng)建 url

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

// 創(chuàng)建請求對象

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

// 發(fā)送異步請求

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

/**

參數(shù):

- data:要反序列化的二進制數(shù)據(jù)

- 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的特點:語法規(guī)則很簡單,且很有邏輯。

#出身名門,w3c制定,微軟和 IBM 曾經(jīng)共同大力推薦過的數(shù)據(jù)格式。

> 專門設計用來傳輸和存儲數(shù)據(jù)

> 與HTML的區(qū)別:HTML的標記不是所有的都需要成對出現(xiàn),

XML要求所有的標記必須成對出現(xiàn)。

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

2、XML解析的方式

> DOM解析

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

* 內(nèi)存消耗極大,不適用于手機。因此蘋果沒有提供手機上DOM解析。

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

> SAX解析

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

* 是蘋果提供的解析方式

* 使用 NSXMLParser 通過 代理 實現(xiàn)解析。

3、SAX解析步驟

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

2> 開始"節(jié)點"

3> 發(fā)現(xiàn)節(jié)點內(nèi)部的內(nèi)容,每一個節(jié)點,可能需要多次才能找完

4> 結(jié)束"節(jié)點"

5> 結(jié)束文檔--解析結(jié)束

#以上步驟,2,3,4會不斷循環(huán),一直到所有解析完成。

4、代碼演示XML的解析。

/**

*? 加載 xml 數(shù)據(jù)

*/

- (void)loadData{

// 創(chuàng)建 url

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

// 創(chuàng)建請求對象

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

// 發(fā)送異步請求(由于解析 xml 是一個耗時的過程,所以新開啟隊列)

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

// 1.創(chuàng)建 xml 解析器

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

// 2.設置代理來解析

parser.delegate = self;

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

[parser parse];

}];

}

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

#pragma mark? - NSXMLParserDelegate 代理方法

/**

*? 開始解析

*/

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

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

}

/**

*? 解析到一個開始節(jié)點調(diào)用

*/

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

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

}

/**

*? 解析到節(jié)點文字調(diào)用? 一個節(jié)點的內(nèi)容可以會調(diào)用多次該方法

*/

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

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

}

/**

*? 解析到結(jié)束節(jié)點調(diào)用? elementName 沒有反斜線 /

*/

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

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

}

/**

*? 解析結(jié)束調(diào)用

*/

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

NSLog(@"5、結(jié)束文檔");

}

5、畫XML解析思維導圖

6、XML解析代碼實現(xiàn)

#pragma mark? - NSXMLParserDelegate 代理方法

/**

*? 開始解析

*/

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

// 清空數(shù)組

[self.videos removeAllObjects];

}

/**

*? 解析到一個開始節(jié)點調(diào)用

*/

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

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

// 創(chuàng)建 video 模型

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

// 設置 id

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

}

// 清空當前正在拼接的字符內(nèi)容

[self.elementString setString:@""];

// 錯誤寫法

//self.elementString = @"";

}

/**

*? 解析到節(jié)點文字調(diào)用? 一個節(jié)點的內(nèi)容可以會調(diào)用多次該方法

*/

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

[self.elementString appendString:string];

}

/**

*? 解析到結(jié)束節(jié)點調(diào)用? elementName 沒有反斜線 /

*/

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

// 如果 elementName 是 video,則添加到數(shù)組中

/* 最笨方法

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是一種以字符串形式間接設置數(shù)據(jù)的方式

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

}

}

/**

*? 解析結(jié)束調(diào)用

*/

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

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

}

四、tableView展示數(shù)據(jù)

1、展示視頻數(shù)據(jù)--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];

// 監(jiān)聽下拉事件

[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中創(chuàng)建UIRefreshControl,設置文字,顏色等信息

4> 系統(tǒng)自動管理UIRefreshControl,自動添加到tableview視圖中;

5> 給UIRefreshControl添加方法,當值改變的時候調(diào)用,方法用于數(shù)據(jù)請求

6> 該方法中請求數(shù)據(jù)確認完成之后,調(diào)用endRefreshing方法,關(guān)閉刷新

五、XML解析重構(gòu)

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

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

// 如果回調(diào)的 block 在當前方法不執(zhí)行,需要用一個屬性記錄

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

// 使用斷言確保外界傳人的回調(diào)不為空,如果為空,則崩潰提示

NSAssert(finished != nil, @"必須傳人回調(diào)");

// 0.創(chuàng)建解析模型

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

// 記錄回調(diào) block

sax.finishedBlock = finished;

// 1.創(chuàng)建 xml 解析器

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

// 2.設置代理來解析

parser.delegate = sax;

// 3.解析器開始解析

// parse:是同步方法,會等所用代理方法執(zhí)行完畢后,才會執(zhí)行后面的代碼

[parser parse];

#warning 不能在parse方法調(diào)用后記錄回調(diào)

// 記錄回調(diào) block

// sax.finishedBlock = finished;

}

六、Copy,NSNumber,id

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

> strong:在設置數(shù)值的時候,僅僅是引用計算+1

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

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

> 如果屬性使用strong:

#下面的代碼

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

#修改為

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

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

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

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

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

_name = [name copy];

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

}

2、NSNumber,id屬性

> 代碼演示實際開發(fā)中不用NSNumber帶來的問題。

> 代碼演示服務器返回數(shù)據(jù)中有id鍵值對的問題。

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

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

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