一、引言
在iOS7後,NSURLSession基本代替了NSURLConnection進行網絡開發,在iOS9後,NSURLConnection相關方法被完全的棄用,iOS系統有向下兼容的特性,盡管NSURLConnection已經被棄用,但在開發中,其方法依然可以被使用,並且如果需要兼容到很低版本的iOS系統,有時就必須使用NSURLConnection類了。
二、使用NSURLConnection進行同步請求
對於網絡請求分為同步和異步兩種,同步是指在請求結果返回之前,程序代碼會卡在請求處,之後的代碼不會被執行,異步是指在發送請求之後,一邊在子線程中接收返回數據,一邊執行之後的代碼,當返回數據接收完畢後,采用回調的方式通知主線程做處理。
使用如下方法進行NSURLConnection的同步請求:
NSURL * url = [NSURL URLWithString:@"http://www.baidu.com"];
打印信息如下圖所示,從中可以看出,當數據返回結束時才執行後面的代碼:
NSURLRequest * request = [NSURLRequest requestWithURL:url];
NSData * data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSLog(@"%@",data);
NSLog(@"繼續執行");
三、使用NSURLConnection進行異步請求
使用同步的方式進行請求有一個很大的弊端,在進行網絡請求時,數據的返回往往需要一定時間,不可能瞬間完成,使用同步的方式將導致界面卡死,沒有提示也不能交互任何用戶操作,這樣的話,很有可能會給用戶程序卡死的假象。
NSURLConnection類提供兩種方式進行異步請求操作。
1.使用block的方式進行異步請求
使用如下代碼進行block方式的異步請求,在block中會傳入請求到的返回數據和數據信息等參數:
NSURL * url = [NSURL URLWithString:@"http://www.baidu.com"];
NSURLRequest * request = [NSURLRequest requestWithURL:url];
//其中的queue參數決定block中的代碼在哪個隊列中執行
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSLog(@"%@",data);
}];
NSLog(@"繼續執行");
2.使用代理回調的異步請求方式
首先遵守協議與生命一個可變的NSData用於接收數據:
@interface ViewController ()<NSURLConnectionDataDelegate>
使用如下的代碼進行請求:
{
NSMutableData * _data;
}
@end
_data = [[NSMutableData alloc]init];
請求發出後,會一次調用如下代理方法進行請求過程的監聽和數據的獲取:
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{
//開始接收數據
[_data setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
//正在接收數據
[_data appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
//接收數據失敗
NSLog(@"%@",error);
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
//接收數據完成
NSLog(@"%@",_data);
}
四、示例
1.通過NSURLConnection進行異步下載:
NSURLConnection 提供了兩種方式來實現連接,一種是同步的另一種是異步的,異步的連接將會創建一個新的線程,這個線程將會來負責下載的動作。而對於同步連接,在下載連接和處理通訊時,則會阻塞當前調用線程。
許多開發者都會認為同步的連接將會堵塞主線程,其實這種觀點是錯誤的。一個同步的連接是會阻塞調用它的線程。如果你在主線程中創建一個同步連接,沒錯,主線程會阻塞。但是如果你並不是從主線程開啟的一個同步的連接,它將會類似異步的連接一樣。因此這種情況並不會堵塞你的主線程。事實上,同步和異步的主要區別就是運行 runtime 為會異步連接創建一個線程,而同步連接則不會。
//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進行同步下載:
使用 NSURLConnection 的 sendSynchronousRequest:returningResponse:error:類方法,我們可以進行同步請求。在創建一個同步的網絡連接的時候我們需要明白一點,並不是是我們的這個同步連接一定會堵塞我們的主線程,如果這個同步的連接是創建在主線程上的,那麼這種情況下是會堵塞我們的主線程的,其他的情況下是不一定會堵塞我們的主線程的。如果你在 GCD 的全局並發隊列上初始化了一個同步的連接,你其實並不會堵塞我們的主線程的。
我們來初始化第一個同步連接,並看看會發生什麼。在實例中,我們將嘗試獲取 Yahoo!美國站點主頁內容:
//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.");
}
查看運行輸出結果,分別為:
synchronous download on main thread without GCD
synchronous download on main thread with GCD
可以看到在主線程上調用同步下載會阻塞當前線程,而使用GCD則不會。