iOS中使用NSURLConnection處理HTTP同步與異步請(qǐng)求
一、引言
在iOS7后,NSURLSession基本代替了NSURLConnection進(jìn)行網(wǎng)絡(luò)開發(fā),在iOS9后,NSURLConnection相關(guān)方法被完全的棄用,iOS系統(tǒng)有向下兼容的特性,盡管NSURLConnection已經(jīng)被棄用,但在開發(fā)中,其方法依然可以被使用,并且如果需要兼容到很低版本的iOS系統(tǒng),有時(shí)就必須使用NSURLConnection類了。
二、使用NSURLConnection進(jìn)行同步請(qǐng)求
對(duì)于網(wǎng)絡(luò)請(qǐng)求分為同步和異步兩種,同步是指在請(qǐng)求結(jié)果返回之前,程序代碼會(huì)卡在請(qǐng)求處,之后的代碼不會(huì)被執(zhí)行,異步是指在發(fā)送請(qǐng)求之后,一邊在子線程中接收返回?cái)?shù)據(jù),一邊執(zhí)行之后的代碼,當(dāng)返回?cái)?shù)據(jù)接收完畢后,采用回調(diào)的方式通知主線程做處理。
使用如下方法進(jìn)行NSURLConnection的同步請(qǐng)求:
NSURL * url = [NSURL URLWithString:@"http://www.baidu.com"];
打印信息如下圖所示,從中可以看出,當(dāng)數(shù)據(jù)返回結(jié)束時(shí)才執(zhí)行后面的代碼:
NSURLRequest * request = [NSURLRequest requestWithURL:url];
NSData * data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSLog(@"%@",data);
NSLog(@"繼續(xù)執(zhí)行");
三、使用NSURLConnection進(jìn)行異步請(qǐng)求
使用同步的方式進(jìn)行請(qǐng)求有一個(gè)很大的弊端,在進(jìn)行網(wǎng)絡(luò)請(qǐng)求時(shí),數(shù)據(jù)的返回往往需要一定時(shí)間,不可能瞬間完成,使用同步的方式將導(dǎo)致界面卡死,沒有提示也不能交互任何用戶操作,這樣的話,很有可能會(huì)給用戶程序卡死的假象。
NSURLConnection類提供兩種方式進(jìn)行異步請(qǐng)求操作。
1.使用block的方式進(jìn)行異步請(qǐng)求
使用如下代碼進(jìn)行block方式的異步請(qǐng)求,在block中會(huì)傳入請(qǐng)求到的返回?cái)?shù)據(jù)和數(shù)據(jù)信息等參數(shù):
NSURL * url = [NSURL URLWithString:@"http://www.baidu.com"];
NSURLRequest * request = [NSURLRequest requestWithURL:url];
//其中的queue參數(shù)決定block中的代碼在哪個(gè)隊(duì)列中執(zhí)行
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSLog(@"%@",data);
}];
NSLog(@"繼續(xù)執(zhí)行");
2.使用代理回調(diào)的異步請(qǐng)求方式
首先遵守協(xié)議與生命一個(gè)可變的NSData用于接收數(shù)據(jù):
@interface ViewController ()<NSURLConnectionDataDelegate>
使用如下的代碼進(jìn)行請(qǐng)求:
{
NSMutableData * _data;
}
@end
_data = [[NSMutableData alloc]init];
請(qǐng)求發(fā)出后,會(huì)一次調(diào)用如下代理方法進(jìn)行請(qǐng)求過程的監(jiān)聽和數(shù)據(jù)的獲取:
NSURL * url = [NSURL URLWithString:@"http://www.baidu.com"];
NSURLRequest * request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
//開始接收數(shù)據(jù)
[_data setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
//正在接收數(shù)據(jù)
[_data appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
//接收數(shù)據(jù)失敗
NSLog(@"%@",error);
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
//接收數(shù)據(jù)完成
NSLog(@"%@",_data);
}
四、示例
1.通過NSURLConnection進(jìn)行異步下載:
NSURLConnection 提供了兩種方式來實(shí)現(xiàn)連接,一種是同步的另一種是異步的,異步的連接將會(huì)創(chuàng)建一個(gè)新的線程,這個(gè)線程將會(huì)來負(fù)責(zé)下載的動(dòng)作。而對(duì)于同步連接,在下載連接和處理通訊時(shí),則會(huì)阻塞當(dāng)前調(diào)用線程。
許多開發(fā)者都會(huì)認(rèn)為同步的連接將會(huì)堵塞主線程,其實(shí)這種觀點(diǎn)是錯(cuò)誤的。一個(gè)同步的連接是會(huì)阻塞調(diào)用它的線程。如果你在主線程中創(chuàng)建一個(gè)同步連接,沒錯(cuò),主線程會(huì)阻塞。但是如果你并不是從主線程開啟的一個(gè)同步的連接,它將會(huì)類似異步的連接一樣。因此這種情況并不會(huì)堵塞你的主線程。事實(shí)上,同步和異步的主要區(qū)別就是運(yùn)行 runtime 為會(huì)異步連接創(chuàng)建一個(gè)線程,而同步連接則不會(huì)。
//asynchronousRequest connection
-(void)fetchAppleHtml{
NSString *urlString = @"http://www.apple.com";
NSURL *url = [NSURL URLWithString:urlString];
// NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:30.0f]; //maximal timeout is 30s
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if ([data length] > 0 && connectionError == nil) {
NSString *documentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [documentsDir stringByAppendingPathComponent:@"apple.html"];
[data writeToFile:filePath atomically:YES];
NSLog(@"Successfully saved the file to %@",filePath);
NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"HTML = %@",html);
}else if ([data length] == 0 && connectionError == nil){
NSLog(@"Nothing was downloaded.");
}else if (connectionError != nil){
NSLog(@"Error happened = %@",connectionError);
}
}];
}
2.通過NSURLConnection進(jìn)行同步下載:
使用 NSURLConnection 的 sendSynchronousRequest:returningResponse:error:類方法,我們可以進(jìn)行同步請(qǐng)求。在創(chuàng)建一個(gè)同步的網(wǎng)絡(luò)連接的時(shí)候我們需要明白一點(diǎn),并不是是我們的這個(gè)同步連接一定會(huì)堵塞我們的主線程,如果這個(gè)同步的連接是創(chuàng)建在主線程上的,那么這種情況下是會(huì)堵塞我們的主線程的,其他的情況下是不一定會(huì)堵塞我們的主線程的。如果你在 GCD 的全局并發(fā)隊(duì)列上初始化了一個(gè)同步的連接,你其實(shí)并不會(huì)堵塞我們的主線程的。
我們來初始化第一個(gè)同步連接,并看看會(huì)發(fā)生什么。在實(shí)例中,我們將嘗試獲取 Yahoo!美國站點(diǎn)主頁內(nèi)容:
//synchronousRequest connection
-(void)fetchYahooData{
NSLog(@"We are here...");
NSString *urlString = @"http://www.yahoo.com";
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSError *error = nil;
NSLog(@"Firing synchronous url connection...");
NSData *data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
if ([data length] > 0 && error == nil) {
NSLog(@"%lu bytes of data was returned.",(unsigned long)[data length]);
}else if([data length] == 0 && error == nil){
NSLog(@"No data was return.");
}else if (error != nil){
NSLog(@"Error happened = %@",error);
}
NSLog(@"We are done.");
}
/*
|
| as we know, it will chock main thread when we call sendSynchronousRequest on main thread,,,,change below
|
v
*/
//call sendSynchronousRequest on GCD pool
-(void)fetchYahooData2_GCD{
NSLog(@"We are here...");
NSString *urlString = @"http://www.yahoo.com";
NSLog(@"Firing synchronous url connection...");
dispatch_queue_t dispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(dispatchQueue, ^{
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
if ([data length] > 0 && error == nil) {
NSLog(@"%lu bytes of data was returned.",(unsigned long)[data length]);
}else if ([data length] == 0 && error == nil){
NSLog(@"No data was returned.");
}else if (error != nil){
NSLog(@"Error happened = %@",error);
}
});
NSLog(@"We are done.");
}
查看運(yùn)行輸出結(jié)果,分別為:
synchronous download on main thread without GCD
synchronous download on main thread with GCD
可以看到在主線程上調(diào)用同步下載會(huì)阻塞當(dāng)前線程,而使用GCD則不會(huì)。
相關(guān)文章
實(shí)例解析iOS應(yīng)用多線程開發(fā)中NSthread類的用法
這篇文章主要介紹了iOS應(yīng)用多線程開發(fā)中NSthread類的用法,代碼基于傳統(tǒng)的Objective-C,NSthread類需要的朋友可以參考下2016-02-02iOS開發(fā)-調(diào)用系統(tǒng)相機(jī)和相冊(cè)獲取照片示例
這篇文章主要介紹了iOS開發(fā)-調(diào)用系統(tǒng)相機(jī)和相冊(cè)獲取照片示例的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-02-02iOS自定義日期、時(shí)間、城市選擇器實(shí)例代碼
這篇文章主要介紹了iOS自定義日期、時(shí)間、城市選擇器實(shí)例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05iOS UISegmentControl實(shí)現(xiàn)自定義分欄效果
這篇文章主要為大家詳細(xì)介紹了iOS UISegmentControl實(shí)現(xiàn)自定義分欄效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03iOS中UITableview錯(cuò)位的問題怎么修復(fù)
這篇文章主要介紹了iOS中UITableview錯(cuò)位的問題以及修復(fù)方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-01-01iOS開發(fā)之MRC(手動(dòng)內(nèi)存管理)詳解
這篇文章主要介紹了?iOS開發(fā)之MRC(手動(dòng)內(nèi)存管理)詳解的相關(guān)資料,需要的朋友可以參考下2022-08-08iOS中管理剪切板的UIPasteboard粘貼板類用法詳解
在iOS中,通過UITextField、UITextView和UIWebView剪切或復(fù)制的內(nèi)容都可以通過UIPasteboard類來管理粘貼操作,下面就為大家?guī)韎OS中管理剪切板的UIPasteboard粘貼板類用法詳解:2016-06-06IOS UI學(xué)習(xí)教程之使用UIImageView控件制作動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了IOS UI學(xué)習(xí)教程之使用UIImageView控件制作動(dòng)畫,感興趣的小伙伴們可以參考一下2016-03-03