NSURLConnection 作為 Core Foundation / CFNetwork 框架的 API 之上的一個抽象,在 2003 年,隨著第一版的 Safari 的發(fā)布就發(fā)布了。NSURLConnection 這個名字,實際上是指代的 Foundation 框架的 URL 加載系統(tǒng)中一系列有關(guān)聯(lián)的組件:NSURLRequest、NSURLResponse、NSURLProtocol、 NSURLCache、 NSHTTPCookieStorage、NSURLCredentialStorage 以及同名類 NSURLConnection。
NSURLRequest 被傳遞給 NSURLConnection。被委托對象(遵守以前的非正式協(xié)議 <NSURLConnectionDelegate> 和 <NSURLConnectionDataDelegate>)異步地返回一個 NSURLResponse 以及包含服務(wù)器返回信息的 NSData。
在一個請求被發(fā)送到服務(wù)器之前,系統(tǒng)會先查詢共享的緩存信息,然后根據(jù)策略(policy)以及可用性(availability)的不同,一個已經(jīng)被緩存的響應(yīng)可能會被立即返回。如果沒有緩存的響應(yīng)可用,則這個請求將根據(jù)我們指定的策略來緩存它的響應(yīng)以便將來的請求可以使用。
在把請求發(fā)送給服務(wù)器的過程中,服務(wù)器可能會發(fā)出鑒權(quán)查詢(authentication challenge),這可以由共享的 cookie 或機(jī)密存儲(credential storage)來自動響應(yīng),或者由被委托對象來響應(yīng)。發(fā)送中的請求也可以被注冊的 NSURLProtocol 對象所攔截,以便在必要的時候無縫地改變其加載行為。
不管怎樣,NSURLConnection 作為網(wǎng)絡(luò)基礎(chǔ)架構(gòu),已經(jīng)服務(wù)了成千上萬的 iOS 和 Mac OS 程序,并且做的還算相當(dāng)不錯。但是這些年,一些用例——尤其是在 iPhone 和 iPad 上面——已經(jīng)對 NSURLConnection 的幾個核心概念提出了挑戰(zhàn),讓蘋果有理由對它進(jìn)行重構(gòu)。
一、NSURLConnection的常用類
(1)NSURL:請求地址
(2)NSURLRequest:封裝一個請求,保存發(fā)給服務(wù)器的全部數(shù)據(jù),包括一個NSURL對象,請求方法、請求頭、請求體....
(3)NSMutableURLRequest:NSURLRequest的子類
(4)NSURLConnection:負(fù)責(zé)發(fā)送請求,建立客戶端和服務(wù)器的連接。發(fā)送NSURLRequest的數(shù)據(jù)給服務(wù)器,并收集來自服務(wù)器的響應(yīng)數(shù)據(jù)
二、NSURLConnection的使用
1.簡單說明
使用NSURLConnection發(fā)送請求的步驟很簡單
(1)創(chuàng)建一個NSURL對象,設(shè)置請求路徑(設(shè)置請求路徑)
(2)傳入NSURL創(chuàng)建一個NSURLRequest對象,設(shè)置請求頭和請求體(創(chuàng)建請求對象)
(3)使用NSURLConnection發(fā)送NSURLRequest(發(fā)送請求)
2.代碼示例
(1)發(fā)送請求的三個步驟:
1.設(shè)置請求路徑
2.創(chuàng)建請求對象
3.發(fā)送請求
3.1發(fā)送同步請求(一直在等待服務(wù)器返回數(shù)據(jù),這行代碼會卡住,如果服務(wù)器,沒有返回數(shù)據(jù),那么在主線程UI會卡住不能繼續(xù)執(zhí)行操作)有返回值
3.2發(fā)送異步請求:沒有返回值
說明:任何NSURLRequest默認(rèn)都是get請求。
(2)發(fā)送同步請求代碼示例:
//
// YYViewController.m
// 01-NSURLConnection的使用(GET)
//
// Created by apple on 14-6-28.
// Copyright (c) 2014年 itcase. All rights reserved.
//
#import "YYViewController.h"
#import "MBProgressHUD+MJ.h"
@interface YYViewController ()
@property (weak, nonatomic) IBOutlet UITextField *username;
@property (weak, nonatomic) IBOutlet UITextField *pwd;
- (IBAction)login;
@end
@implementation YYViewController
- (IBAction)login {
// 1.提前的表單驗證
if (self.username.text.length==0) {
[MBProgressHUD showError:@"請輸入用戶名"];
return;
}
if (self.pwd.text.length==0) {
[MBProgressHUD showError:@"請輸入密碼"];
return;
}
// 2.發(fā)送請求給服務(wù)器(帶上賬號和密碼)
//添加一個遮罩,禁止用戶操作
// [MBProgressHUD showMessage:@"正在努力加載中...."];
// GET請求:請求行\(zhòng)請求頭\請求體
//
// 1.設(shè)置請求路徑
NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.53:8080/MJServer/login?username=%@&pwd=%@",self.username.text,self.pwd.text];
NSURL *url=[NSURL URLWithString:urlStr];
// 2.創(chuàng)建請求對象
NSURLRequest *request=[NSURLRequest requestWithURL:url];
// 3.發(fā)送請求
//發(fā)送同步請求,在主線程執(zhí)行
NSData *data=[NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
//(一直在等待服務(wù)器返回數(shù)據(jù),這行代碼會卡住,如果服務(wù)器沒有返回數(shù)據(jù),那么在主線程UI會卡住不能繼續(xù)執(zhí)行操作)
NSLog(@"--%d--",data.length);
}
@end
模擬器情況:
打印服務(wù)器返回的信息:
補(bǔ)充說明:
1.提前的表單驗證
2.發(fā)送請求給服務(wù)器(帶上賬號和密碼)
GET請求:請求行\(zhòng)請求頭\請求體
注意:GET請求中不存在請求體,因為所有的信息都寫在URL里面。在IOS里面,請求行和請求頭都不用寫。
(3)發(fā)送異步請求
發(fā)送異步請求有兩種方式:
1)使用block回調(diào)
2)代理
A.使用block回調(diào)方法發(fā)送異步請求
使用block回調(diào)代碼示例:
//
// YYViewController.m
// 01-NSURLConnection的使用(GET)
//
// Created by apple on 14-6-28.
// Copyright (c) 2014年 itcase. All rights reserved.
//
#import "YYViewController.h"
#import "MBProgressHUD+MJ.h"
@interface YYViewController ()
@property (weak, nonatomic) IBOutlet UITextField *username;
@property (weak, nonatomic) IBOutlet UITextField *pwd;
- (IBAction)login;
@end
@implementation YYViewController
- (IBAction)login {
// 1.提前的表單驗證
if (self.username.text.length==0) {
[MBProgressHUD showError:@"請輸入用戶名"];
return;
}
if (self.pwd.text.length==0) {
[MBProgressHUD showError:@"請輸入密碼"];
return;
}
// 2.發(fā)送請求給服務(wù)器(帶上賬號和密碼)
//添加一個遮罩,禁止用戶操作
[MBProgressHUD showMessage:@"正在努力加載中...."];
//
// 1.設(shè)置請求路徑
NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.53:8080/MJServer/login?username=%@&pwd=%@",self.username.text,self.pwd.text];
NSURL *url=[NSURL URLWithString:urlStr];
// 2.創(chuàng)建請求對象
NSURLRequest *request=[NSURLRequest requestWithURL:url];
// 3.發(fā)送請求
//3.1發(fā)送同步請求,在主線程執(zhí)行
// NSData *data=[NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
//(一直在等待服務(wù)器返回數(shù)據(jù),這行代碼會卡住,如果服務(wù)器沒有返回數(shù)據(jù),那么在主線程UI會卡住不能繼續(xù)執(zhí)行操作)
//3.1發(fā)送異步請求
//創(chuàng)建一個隊列(默認(rèn)添加到該隊列中的任務(wù)異步執(zhí)行)
// NSOperationQueue *queue=[[NSOperationQueue alloc]init];
//獲取一個主隊列
NSOperationQueue *queue=[NSOperationQueue mainQueue];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSLog(@"--block回調(diào)數(shù)據(jù)--%@---%d", [NSThread currentThread],data.length);
//隱藏HUD,刷新UI的操作一定要放在主線程執(zhí)行
[MBProgressHUD hideHUD];
//解析data
/*
{"success":"登錄成功"}
{"error":"用戶名不存在"}
{"error":"密碼不正確"}
*/
NSDictionary *dict=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
NSLog(@"%@",dict);
//判斷后,在界面提示登錄信息
NSString *error=dict[@"error"];
if (error) {
[MBProgressHUD showError:error];
}else
{
NSString *success=dict[@"success"];
[MBProgressHUD showSuccess:success];
}
}];
NSLog(@"請求發(fā)送完畢");
}
@end
模擬器情況(注意這里使用了第三方框架):
打印查看:
代碼說明:
block代碼段:當(dāng)服務(wù)器有返回數(shù)據(jù)的時候調(diào)用會開一條新的線程去發(fā)送請求,主線程繼續(xù)往下走,當(dāng)拿到服務(wù)器的返回數(shù)據(jù)的數(shù)據(jù)的時候再回調(diào)block,執(zhí)行block代碼段。這種情況不會卡住主線程。
隊列的作用:決定這個block操作放在哪個線程執(zhí)行?
刷新UI界面的操作應(yīng)該放在主線程執(zhí)行,不能放在子線程,在子線程處理UI相關(guān)操作會出現(xiàn)一些莫名的問題。
提示:
(1)創(chuàng)建一個操作,放在NSOperation隊列中執(zhí)行,默認(rèn)是異步執(zhí)行的。
(2)mainqueue 返回一個和主線程相關(guān)的隊列,即主隊列。
新的問題:如果向服務(wù)器發(fā)送請求,卻并沒有拿到數(shù)據(jù),那么程序會崩潰(data不能為空)
改進(jìn)代碼:
NSOperationQueue *queue=[NSOperationQueue mainQueue];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
//當(dāng)請求結(jié)束的時候調(diào)用(有兩種結(jié)果,一個是成功拿到數(shù)據(jù),也可能沒有拿到數(shù)據(jù),請求失敗)
NSLog(@"--block回調(diào)數(shù)據(jù)--%@---%d", [NSThread currentThread],data.length);
//隱藏HUD,刷新UI的操作一定要放在主線程執(zhí)行
[MBProgressHUD hideHUD];
//解析data
/*
{"success":"登錄成功"}
{"error":"用戶名不存在"}
{"error":"密碼不正確"}
*/
if (data) {//請求成功
NSDictionary *dict=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
NSLog(@"%@",dict);
//判斷后,在界面提示登錄信息
NSString *error=dict[@"error"];
if (error) {
[MBProgressHUD showError:error];
}else
{
NSString *success=dict[@"success"];
[MBProgressHUD showSuccess:success];
}
}else //請求失敗
{
[MBProgressHUD showError:@"網(wǎng)絡(luò)繁忙,請稍后重試!"];
}
}];
解析data
//解析data
/*
{"success":"登錄成功"}
{"error":"用戶名不存在"}
{"error":"密碼不正確"}
*/
說明:使用NSJSONSerialization 返回的對象,取決于最外層是什么,如果是{}那就是字典,[]那就是數(shù)組等。
補(bǔ)充說明:
首先確定請求路徑,然后創(chuàng)建請求對象(默認(rèn)發(fā)送的時get請求),使用異步方法(一調(diào)用這個方法,它會自動開啟一個子線程去發(fā)送請求,當(dāng)請求成功,數(shù)據(jù)返回的時候自動調(diào)用內(nèi)部的代碼段,這個代碼段在那個線程執(zhí)行取決于隊列,如果是主隊列,那么在子線程發(fā)送請求成功拿到服務(wù)器的數(shù)據(jù)后,回到主線程中解析數(shù)據(jù),刷新UI界面)。
B.使用代理方法發(fā)送異步請求
要監(jiān)聽服務(wù)器返回的data,所以使用<NSURLConnectionDataDelegate>協(xié)議
常見大代理方法如下:
#pragma mark- NSURLConnectionDataDelegate代理方法
//當(dāng)接收到服務(wù)器的響應(yīng)(連通了服務(wù)器)時會調(diào)用
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
//當(dāng)接收到服務(wù)器的數(shù)據(jù)時會調(diào)用(可能會被調(diào)用多次,每次只傳遞部分?jǐn)?shù)據(jù))
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
//當(dāng)服務(wù)器的數(shù)據(jù)加載完畢時就會調(diào)用
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
//請求錯誤(失敗)的時候調(diào)用(請求超時\斷網(wǎng)\沒有網(wǎng)\,一般指客戶端錯誤)
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
使用異步方法發(fā)送get請求的代碼示例:
//
// YYViewController.m
// 01-NSURLConnection的使用(GET)
//
// Created by apple on 14-6-28.
// Copyright (c) 2014年 itcase. All rights reserved.
//
#import "YYViewController.h"
#import "MBProgressHUD+MJ.h"
@interface YYViewController ()<NSURLConnectionDataDelegate>
@property (weak, nonatomic) IBOutlet UITextField *username;
@property (weak, nonatomic) IBOutlet UITextField *pwd;
@property(nonatomic,strong)NSMutableData *responseData;
- (IBAction)login;
@end
@implementation YYViewController
- (IBAction)login {
// 1.提前的表單驗證
if (self.username.text.length==0) {
[MBProgressHUD showError:@"請輸入用戶名"];
return;
}
if (self.pwd.text.length==0) {
[MBProgressHUD showError:@"請輸入密碼"];
return;
}
// 2.發(fā)送請求給服務(wù)器(帶上賬號和密碼)
//添加一個遮罩,禁止用戶操作
[MBProgressHUD showMessage:@"正在努力加載中...."];
//
// 2.1設(shè)置請求路徑
NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.53:8080/MJServer/login?username=%@&pwd=%@",self.username.text,self.pwd.text];
NSURL *url=[NSURL URLWithString:urlStr];
// 2.2創(chuàng)建請求對象
// NSURLRequest *request=[NSURLRequest requestWithURL:url];//默認(rèn)就是GET請求
//設(shè)置請求超時
NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:url];
request.timeoutInterval=5.0;
// 2.3.發(fā)送請求
//使用代理發(fā)送異步請求(通常應(yīng)用于文件下載)
NSURLConnection *conn=[NSURLConnection connectionWithRequest:request delegate:self];
[conn start];
NSLog(@"已經(jīng)發(fā)出請求---");
}
#pragma mark- NSURLConnectionDataDelegate代理方法
/*
*當(dāng)接收到服務(wù)器的響應(yīng)(連通了服務(wù)器)時會調(diào)用
*/
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(@"接收到服務(wù)器的響應(yīng)");
//初始化數(shù)據(jù)
self.responseData=[NSMutableData data];
}
/*
*當(dāng)接收到服務(wù)器的數(shù)據(jù)時會調(diào)用(可能會被調(diào)用多次,每次只傳遞部分?jǐn)?shù)據(jù))
*/
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(@"接收到服務(wù)器的數(shù)據(jù)");
//拼接數(shù)據(jù)
[self.responseData appendData:data];
NSLog(@"%d---%@--",self.responseData.length,[NSThread currentThread]);
}
/*
*當(dāng)服務(wù)器的數(shù)據(jù)加載完畢時就會調(diào)用
*/
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(@"服務(wù)器的數(shù)據(jù)加載完畢");
//隱藏HUD
[MBProgressHUD hideHUD];
//處理服務(wù)器返回的所有數(shù)據(jù)
NSDictionary *dict=[NSJSONSerialization JSONObjectWithData:self.responseData options:NSJSONReadingMutableLeaves error:nil];
//判斷后,在界面提示登錄信息
NSString *error=dict[@"error"];
if (error) {
[MBProgressHUD showError:error];
}else
{
NSString *success=dict[@"success"];
[MBProgressHUD showSuccess:success];
}
NSLog(@"%d---%@--",self.responseData.length,[NSThread currentThread]);
}
/*
*請求錯誤(失敗)的時候調(diào)用(請求超時\斷網(wǎng)\沒有網(wǎng)\,一般指客戶端錯誤)
*/
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// NSLog(@"請求錯誤");
//隱藏HUD
[MBProgressHUD hideHUD];
[MBProgressHUD showError:@"網(wǎng)絡(luò)繁忙,請稍后重試!"];
}
@end
打印查看:
補(bǔ)充:
(1)數(shù)據(jù)的處理
在didReceiveData:方法中,拼接接收到的所有數(shù)據(jù),等所有數(shù)據(jù)都拿到后,在connectionDidFinishLoading:方法中進(jìn)行處理
(2)網(wǎng)絡(luò)延遲
在做網(wǎng)絡(luò)開發(fā)的時候,一定要考慮到網(wǎng)絡(luò)延遲情況的處理,可以在服務(wù)器的代碼設(shè)置一個斷點模擬。
在服務(wù)器代碼的登錄方法中設(shè)置斷點
設(shè)置請求的最大延遲
模擬器情況:
打印查看:
三、NSMutableURLRequest
NSMutableURLRequest是NSURLRequest的子類,常用方法有
設(shè)置請求超時等待時間(超過這個時間就算超時,請求失敗)
設(shè)置請求方法(比如GET和POST)
設(shè)置請求體
設(shè)置請求頭