iOS網(wǎng)絡(luò)1——NSURLConnection使用詳解

閱讀目錄

一、整體介紹

二、使用的一般步驟

三、舉例說明

四、NSData 數(shù)據(jù)的反序列化

注意事項:

iOS9訪問http網(wǎng)頁

原文在此

一、整體介紹

NSURLConnection是蘋果提供的原生網(wǎng)絡(luò)訪問類,但是蘋果很快會將其廢棄,且由NSURLSession(iOS7以后)來替代。目前使用最廣泛的第三方網(wǎng)絡(luò)框架AFNetworking最新版本已棄用了NSURLConnection,那我們學(xué)習(xí)它還有什么用呢?

首先,蘋果棄用它還是需要時間的,最起碼到iOS10之后;

現(xiàn)在還有一些老項目會使用NSURLConnection,特別是2013年之前的項目,用戶量基礎(chǔ)還是很大的;

另外,不得不承認(rèn),有些公司還在用類似ASI這些經(jīng)典的網(wǎng)絡(luò)框架,所以還是很有必要學(xué)習(xí)NSURLConnection的。

二、使用的一般步驟

1NSURL:請求地址,定義一個網(wǎng)絡(luò)資源路徑:

NSURL*url = [NSURLURLWithString:@"協(xié)議://主機(jī)地址/路徑?參數(shù)&參數(shù)"];

解釋如下:

協(xié)議:不同的協(xié)議,代表著不同的資源查找方式、資源傳輸方式,比如常用的http,ftp等

主機(jī)地址:存放資源的主機(jī)的IP地址(域名)

路徑:資源在主機(jī)中的具體位置

參數(shù):參數(shù)可有可無,也可以多個。如果帶參數(shù)的話,用“?”號后面接參數(shù),多個參數(shù)的話之間用&隔開

2NSURLRequest:請求,根據(jù)前面的NSURL建立一個請求:

NSURLRequest*request = [NSURLRequestrequestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicytimeoutInterval:15.0];

參數(shù)解釋如下:

url:資源路徑

cachePolicy:緩存策略(無論使用哪種緩存策略,都會在本地緩存數(shù)據(jù)),類型為美劇類型,取值如下:

NSURLRequestUseProtocolCachePolicy = 0 //默認(rèn)的緩存策略,使用協(xié)議的緩存策略

NSURLRequestReloadIgnoringLocalCacheData = 1 //每次都從網(wǎng)絡(luò)加載

NSURLRequestReturnCacheDataElseLoad = 2 //返回緩存否則加載,很少使用

NSURLRequestReturnCacheDataDontLoad = 3 //只返回緩存,沒有也不加載,很少使用

timeoutInterval:超時時長,默認(rèn)60s

另外,還可以設(shè)置其它一些信息,比如請求頭,請求體等等,如下:

注意,下面的request應(yīng)為NSMutableURLRequest,即可變類型

// 告訴服務(wù)器數(shù)據(jù)為json類型[request setValue:@"application/json"forHTTPHeaderField:@"Content-Type"];// 設(shè)置請求體(json類型)NSData*jsonData = [NSJSONSerializationdataWithJSONObject:@{@"userid":@"123456"} options:NSJSONWritingPrettyPrintederror:nil];request.HTTPBody = jsonData;

3 發(fā)送請求:

NSURLConnection默認(rèn)的請求類型為GET,下面分異步和同步兩種介紹

異步請求

[NSURLConnectionsendAsynchronousRequest:request queue:[[NSOperationQueuealloc] init] completionHandler:^(NSURLResponse*response,NSData*data,NSError*connectionError) {// 有的時候,服務(wù)器訪問正常,但是會沒有數(shù)據(jù)!// 以下的 if 是比較標(biāo)準(zhǔn)的錯誤 處理代碼!if(connectionError !=nil|| data ==nil) {//給用戶的提示信息NSLog(@"網(wǎng)絡(luò)不給力");return;? ? }}];

參數(shù)說明如下:

completionHandler:請求響應(yīng)后(或者請求超時)執(zhí)行的代碼,queue為代碼添加到的隊列,即block執(zhí)行的線程

NSURLResponse 為服務(wù)器的響應(yīng),真實類型為NSHTTPURLResponse,通常只在“下載”功能時,才會使用;下面是協(xié)議頭的參數(shù):

URL:響應(yīng)的URL,有的時候,訪問一個URL地址,服務(wù)器可能會出現(xiàn)重定向,會定位到新的地址!

MIMEType(Content-Type):服務(wù)器告訴客戶端,可以用什么軟件打開二進(jìn)制數(shù)據(jù)!網(wǎng)絡(luò)之所以豐富多采,是因為有豐富的客戶端軟件!栗子:windows上提示安裝 Flash 插件

expectedContentLength:預(yù)期的內(nèi)容長度,要下載的文件長度,下載文件時非常有用

suggestedFilename:"建議"的文件名,方便用戶直接保存,很多時候,用戶并不關(guān)心要保存成什么名字!

textEncodingName:文本的編碼名稱 @"UTF8",大多數(shù)都是 UTF8

statusCode:狀態(tài)碼,在做下載操作的時候,需要判斷一下

allHeaderFields:所有的響應(yīng)頭字典時候,用戶并不關(guān)心要保存成什么名字!

NSData 服務(wù)器返回的數(shù)據(jù),例如json、xml(現(xiàn)在用的少)

NSError 網(wǎng)絡(luò)訪問錯誤碼

注意,block的執(zhí)行線程為queue,如果block涉及到UI操作,則必須回到主線程:[NSOperationQueue mainQueue]

發(fā)送同步請求

// 同步請求,代碼會阻塞在這里一直等待服務(wù)器返回,如果data為nil則請求失敗,當(dāng)獲取少量數(shù)據(jù)時可以使用此方法。// request參數(shù)同上。NSData*data = [NSURLConnectionsendSynchronousRequest:request returningResponse:nilerror:nil];

三、舉例說明

分三個部分:

網(wǎng)絡(luò)請求(json、xml數(shù)據(jù))

文件下載

文件上傳,這里例子不再給出,請參考這里:http://www.cnblogs.com/mddblog/p/5215453.html

1 一般的網(wǎng)絡(luò)請求

/// 兩種請求方式typedefenum{? ? MethodGET,? ? MethodPOST}Method;/// 請求成功后,直接將jsonData轉(zhuǎn)為jsonDict+ (void)connectionRequestWithMethod:(Method)method? URLString:(NSString*)URLString parameters:(NSDictionary*)dict success:(void(^)(idJSON))success fail:(void(^)(NSError*error))fail {// 簡單的轉(zhuǎn)碼,如果參數(shù)帶有?&特殊字符,下面方法不適合URLString = [URLString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];// 1.創(chuàng)建請求NSURL*url = [NSURLURLWithString:URLString];NSMutableURLRequest*request = [NSMutableURLRequestrequestWithURL:url];// 請求方式,默認(rèn)為GETif(method == MethodPOST) {? ? ? ? request.HTTPMethod =@"POST";? ? }// 根據(jù)需要設(shè)置[request setValue:@"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"forHTTPHeaderField:@"Accept"];// 2.設(shè)置請求頭 Content-Type? 返回格式[request setValue:@"application/json"forHTTPHeaderField:@"Content-Type"];// 3.設(shè)置請求體 NSDictionary --> NSDataif(dict !=nil) {NSData*data = [NSJSONSerializationdataWithJSONObject:dict options:NSJSONWritingPrettyPrintederror:nil];? ? ? ? request.HTTPBody = data;? ? }// 4.發(fā)送請求[NSURLConnectionsendAsynchronousRequest:request queue:[NSOperationQueuemainQueue] completionHandler:^(NSURLResponse*response,NSData*data,NSError*connectionError) {if((data !=nil) && (connectionError ==nil)) {NSDictionary*jsonDict = [NSJSONSerializationJSONObjectWithData:data options:NSJSONReadingMutableContainerserror:nil];if(success) {? ? ? ? ? ? ? ? success(jsonDict);? ? ? ? ? ? }? ? ? ? }else{if(fail) {? ? ? ? ? ? ? ? fail(connectionError);? ? ? ? ? ? }? ? ? ? }? ? ? ? ? ? }];}

2 文件下載

下面舉一個實現(xiàn)斷點(diǎn)續(xù)傳的網(wǎng)絡(luò)下載,要實現(xiàn)斷點(diǎn)續(xù)傳,還需要用到NSURLConnectionDataDelegate代理,NSFileHandle文件操作或者數(shù)據(jù)流的形式(NSOutputStream),這里以NSFileHandle為例,訪問的服務(wù)器采用Apache搭建的本地服務(wù)器,具體見代碼:

@interfaceViewController() /// 文件下載完畢之后,在本地保存的路徑@property(nonatomic,copy)NSString*filePath;/// 需要下載的文件的總大小!@property(nonatomic,assign)longlongserverFileLength;/// 當(dāng)前已經(jīng)下載長度@property(nonatomic,assign)longlonglocalFileLength;@end@implementationViewController- (void)viewDidLoad {? ? [superviewDidLoad];? ? }/// 點(diǎn)擊屏幕事件- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event {? ? [selfdownloadFile];}/// 下載文件- (void)downloadFile {// 文件存儲路徑self.filePath =@"/Users/username/Desktop//陶喆 - 愛很簡單.mp3";// 要下載的網(wǎng)絡(luò)文件,采用Apache搭建的本地服務(wù)器NSString*urlStr =@"http://localhost/陶喆 - 愛很簡單.mp3";// 簡單的轉(zhuǎn)碼,如果參數(shù)帶有?&特殊字符,下面方法不適合urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];NSURL*url = [NSURLURLWithString:urlStr];NSMutableURLRequest*request = [NSMutableURLRequestrequestWithURL:url];// 設(shè)置請求方法為 HEAD 方法,這里只是頭,數(shù)據(jù)量少,用同步請求也可以,不過最好還是用異步請求request.HTTPMethod =@"HEAD";// 無論是否會引起循環(huán)引用,block里面都使用弱引用__weaktypeof(self) weakSelf =self;? ? [NSURLConnectionsendAsynchronousRequest:request queue:[[NSOperationQueuealloc] init] completionHandler:^(NSURLResponse* _Nullable response,NSData* _Nullable data,NSError* _Nullable connectionError) {// 出錯則返回if(connectionError !=nil) {NSLog(@"connectionError = %@",connectionError);return;? ? ? ? }// 記錄需要下載的文件總長度longlongserverFileLength = response.expectedContentLength;? ? ? ? weakSelf.serverFileLength = serverFileLength;// 初始化已下載文件大小weakSelf.localFileLength =0;NSDictionary*dict = [[NSFileManagerdefaultManager] attributesOfItemAtPath:weakSelf.filePath error:NULL];longlonglocalFileLength = [dict[NSFileSize] longLongValue];// 如果沒有本地文件,直接下載!if(!localFileLength) {// 下載新文件[weakSelf getFileWithUrlString:urlStr];? ? ? ? }// 如果已下載的大小,大于服務(wù)器文件大小,肯定出錯了,刪除文件并從新下載if(localFileLength > serverFileLength) {// 刪除文件 remove[[NSFileManagerdefaultManager]? removeItemAtPath:self.filePath error:NULL];// 下載新文件[selfgetFileWithUrlString:urlStr];? ? ? ? ? ? ? ? ? ? }elseif(localFileLength == serverFileLength) {NSLog(@"文件已經(jīng)下載完畢");? ? ? ? }elseif(localFileLength && localFileLength < serverFileLength) {// 文件下載了一半,則使用斷點(diǎn)續(xù)傳self.localFileLength = localFileLength;? ? ? ? ? ? ? ? ? ? ? ? [selfgetFileWithUrlString:urlStr WithStartSize:localFileLength endSize:serverFileLength-1];? ? ? ? }? ? }];? ? }/// 重新開始下載文件(代理方法監(jiān)聽下載過程)-(void)getFileWithUrlString:(NSString*)urlString{NSURL*url = [NSURLURLWithString:urlString];// 默認(rèn)就是 GET 請求NSURLRequest*request = [NSURLRequestrequestWithURL:url];// 開始請求,并設(shè)置代理為selfNSURLConnection*conn = [[NSURLConnectionalloc] initWithRequest:request delegate:self];// 設(shè)置代理的執(zhí)行線程,一般不在主線程[conn setDelegateQueue:[[NSOperationQueuealloc] init]];// NSUrlConnection 的代理方法是一個特殊的事件源!// 開啟運(yùn)行循環(huán)!CFRunLoopRun();}/* 斷點(diǎn)續(xù)傳(代理方法監(jiān)聽下載過程)

* startSize:本次斷點(diǎn)續(xù)傳開始的位置

* endSize:本地斷點(diǎn)續(xù)傳結(jié)束的位置

*/-(void)getFileWithUrlString:(NSString*)urlString WithStartSize:(longlong)startSize endSize:(longlong)endSize{// 1. 創(chuàng)建請求!NSURL*url = [NSURLURLWithString:urlString];// 默認(rèn)就是 GET 請求NSMutableURLRequest*request = [NSMutableURLRequestrequestWithURL:url];// 設(shè)置斷點(diǎn)續(xù)傳信息NSString*range = [NSStringstringWithFormat:@"Bytes=%lld-%lld",startSize,endSize];? ? [request setValue:range forHTTPHeaderField:@"Range"];// NSUrlConnection 下載過程!NSURLConnection*conn = [[NSURLConnectionalloc] initWithRequest:request delegate:self];// 設(shè)置代理的執(zhí)行線程// 傳一個非主隊列![conn setDelegateQueue:[[NSOperationQueuealloc] init]];// NSUrlConnection 的代理方法是一個特殊的事件源!// 開啟運(yùn)行循環(huán)!CFRunLoopRun();}#pragma mark - NSURLConnectionDataDelegate/// 1. 接收到服務(wù)器響應(yīng)- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response {// 服務(wù)器響應(yīng)// response.expectedContentLength的大小是要下載文件的大小,而不是文件的總大小。比如請求頭設(shè)置了Range[requestM setValue:rangeStr forHTTPHeaderField:@"Range"];則返回的大小為range范圍內(nèi)的大小}/// 2. 接收到數(shù)據(jù)- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {//data當(dāng)前接收到的網(wǎng)絡(luò)數(shù)據(jù);// 如果這個文件不存在,響應(yīng)的文件句柄就不會創(chuàng)建!NSFileHandle*fileHandle = [NSFileHandlefileHandleForWritingAtPath:self.filePath];// 在這個路徑下已經(jīng)有文件了!if(fileHandle) {// 將文件句柄移動到文件的末尾[fileHandle seekToEndOfFile];// 寫入文件的意思(會將data寫入到文件句柄所操縱的文件!)[fileHandle writeData:data];? ? ? ? ? ? ? ? [fileHandle closeFile];? ? ? ? ? ? }else{// 第一次調(diào)用這個方法的時候,在本地還沒有文件路徑(沒有這個文件)![data writeToFile:self.filePath atomically:YES];? ? }}/// 3. 接收完成- (void)connectionDidFinishLoading:(NSURLConnection*)connection {NSLog(@"下載完成");}/// 4. 網(wǎng)絡(luò)錯誤- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error {NSLog(@"下載錯誤 %@", error);}@end

四、NSData 數(shù)據(jù)的反序列化

服務(wù)器返回的NSData數(shù)據(jù)類型,事先已經(jīng)知道,因此可以直接返序列化為實際的類型,例如:json(字典或數(shù)組)、.plist(字典或數(shù)組)、text、xml等:

加載數(shù)據(jù)

// 加載本地NSString*path = [[NSBundlemainBundle] pathForResource:@"文件名 "ofType:nil];NSData*jsonData = [NSDatadataWithContentsOfFile:path];// 從網(wǎng)絡(luò)直接加載NSURL*url = [[NSBundlemainBundle] URLForResource:@"topic_news.json"withExtension:nil];NSData*data = [NSDatadataWithContentsOfURL:url];

.plist反序列化

這種很少使用,只是蘋果自己的格式

// 關(guān)于選型參數(shù)// NSPropertyListImmutable = 0,不可變// NSPropertyListMutableContainers = 1 << 0,容器可變// NSPropertyListMutableContainersAndLeaves = 1 << 1,容器和葉子可變// 通常后續(xù)直接做字典轉(zhuǎn)模型,不需要關(guān)心是否可變,所以一般直接賦值為0idresult = [NSPropertyListSerializationpropertyListWithData:data options:0format:NULLerror:NULL];

關(guān)于json數(shù)據(jù)

json數(shù)據(jù)的本質(zhì)是字符串,根格式只有兩種可能:數(shù)組或字典。

反序列化:從服務(wù)器接收到的二進(jìn)制數(shù)據(jù) 轉(zhuǎn)換成 字典或者數(shù)組

// 假設(shè)json數(shù)據(jù)位:[{鍵值對},{鍵值對}],則可以將數(shù)據(jù)轉(zhuǎn)化為字典或數(shù)組NSArray*dictArray = [NSJSONSerializationJSONObjectWithData:jsonData options:NSJSONReadingMutableContainerserror:nil];

序列化:將字典或者數(shù)組 轉(zhuǎn)換成 二進(jìn)制數(shù)據(jù),準(zhǔn)備發(fā)送給服務(wù)器

// obj為字典或數(shù)組NSData*data = [NSJSONSerializationdataWithJSONObject:obj options:0error:NULL];// 轉(zhuǎn)化前可以判斷是否可以序列化:+ (BOOL)isValidJSONObject:(id)obj;

一個對象能夠被轉(zhuǎn)換成 JSON 必須符合以下條件:

頂級節(jié)點(diǎn),必須是一個 NSArray or NSDictionary

所有的對象必須是 NSString, NSNumber, NSArray, NSDictionary, or NSNull

所有字典的 key 都必須是 NSString

NSNumber 不能為空或者無窮大

注意事項:

請求的緩存策略使用 NSURLRequestReloadIgnoringCacheData,忽略本地緩存

服務(wù)器響應(yīng)結(jié)束后,要記錄 Etag,服務(wù)器內(nèi)容和本地緩存對比是否變化的重要依據(jù)!

在發(fā)送請求時,設(shè)置 If-None-Match,并且傳入 etag

連接結(jié)束后,要判斷響應(yīng)頭的狀態(tài)碼,如果是 304,說明本地緩存內(nèi)容沒有發(fā)生變化,此時可以使用本地緩存來加載數(shù)據(jù),如果緩存文件被意外刪除,程序依然運(yùn)行但會報錯,加載數(shù)據(jù)也失?。篘SCachedURLResponse *cachedURLRes = [[NSURLCache sharedURLCache] cachedResponseForRequest:requestM];//cachedURLRes為nil

GET 緩存的數(shù)據(jù)會保存在 Cache 目錄中 /bundleId 下,Cache.db 中

iOS9訪問http網(wǎng)頁

在Info.plist中添加NSAppTransportSecurity類型Dictionary;

在NSAppTransportSecurity下添加NSAllowsArbitraryLoads類型Boolean,值設(shè)為YES

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,241評論 2 375

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