NSURLConnection提供了簡單的接口來創(chuàng)建和取消一個連接,并支持一個代理方法的集合來提供連接的響應(yīng),并對連接進行多方面的控制。這個類的方法可以分為5大類:URL加載、緩存管理、認證與證書、cookie存儲、協(xié)議支持。
創(chuàng)建一個連接
NSURLConnection提供了三種方式來獲取URL的內(nèi)容:同步、異步使用完成處理器block、異步使用自定義的代理對象。
使用同步請求時,一般是在后臺線程中獨占線程運行,我們可以調(diào)用sendSynchronousRequest:returningResponse:error: 方法來執(zhí)行HTTP請求。當(dāng)請求完成或返回錯誤時,該方法會返回。
如果我們不需要監(jiān)聽請求的狀態(tài),而只是在數(shù)據(jù)完成返回時執(zhí)行一些操作,則可以調(diào)用sendAsynchronousRequest:queue:completionHandler:方法來執(zhí)行一個異步操作,其中需要傳遞一個block來處理結(jié)果。
我們也可以創(chuàng)建一個代理對象來處理異步請求,此時我們需要實現(xiàn)以下方法:connection:didReceiveResponse:、connection:didReceiveData:、connection:didFailWithError:和connectionDidFinishLoading: 。這些方法在NSURLConnectionDelegate、NSURLConnectionDownloadDelegate和 NSURLConnectionDataDelegate協(xié)議中定義。
代碼清單1以代理對象異步請求為例,初始化了一個URL連接并實現(xiàn)代理方法來處理連接響應(yīng)
@interface Conn : NSObject
{
NSURLConnection *theConnection;
NSMutableData *receivedData;
}
@end
@implementation Conn
- (void)createConnection
{
// 創(chuàng)建一個請求
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.apple.com/"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// 創(chuàng)建NSMutableData來保存接收到的數(shù)據(jù)
receivedData = [NSMutableData dataWithCapacity: 0];
// 使用theRequest創(chuàng)建一個連接并開始加載數(shù)據(jù)
// 調(diào)用initWithRequest:delegate后會立即開始傳輸
// 請求可以在connectionDidFinishLoading:或connection:didFailWithError:消息被發(fā)送前通過調(diào)用cancel來取消
theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (!theConnection) {
// 釋放receivedData對象
receivedData = nil;
// 通知用戶連接失敗
}
}
// 當(dāng)服務(wù)端提供了有效的數(shù)據(jù)來創(chuàng)建NSURLResponse對象時,代理會收到connection:didReceiveResponse:消息。
// 這個代理方法會檢查NSURLResponse對象并確認數(shù)據(jù)的content-type,MIME類型,文件 名和其它元數(shù)據(jù)。
// 需要注意的是,對于單個連接,我們可能會接多次收到connection:didReceiveResponse:消息;這咱情況發(fā)生在
// 響應(yīng)是多重MIME編碼的情況下。每次代理接收到connection:didReceiveResponse:時,應(yīng)該重設(shè)進度標(biāo)識
// 并丟棄之前接收到的數(shù)據(jù)。
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[receivedData setLength:0];
}
// 代理會定期接收到connection:didReceiveData:消息,該消息用于接收服務(wù)端返回的數(shù)據(jù)實體。該方法負責(zé)存儲數(shù)據(jù)。
// 我們也可以用這個方法提供進度信息,這種情況下,我們需要在connection:didReceiveResponse:方法中
// 調(diào)用響應(yīng)對象的expectedContentLength方法來獲取數(shù)據(jù)的總長度。
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[receivedData appendData:data];
}
// 如果數(shù)據(jù)傳輸?shù)倪^程中出現(xiàn)了錯誤,代理會接收到connection:didFailWithError:消息。其中error參數(shù)給出了錯誤信息。
// 在代理收到connection:didFailWithError:消息后,它不會再接收指定連接的代理消息。
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
theConnection = nil;
receivedData = nil;
NSLog(@"Connection failed! Error - %@ %@", [error localizedDescription], [[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
// 如果成功獲取服務(wù)端返回的所有數(shù)據(jù),則代理會收到connectionDidFinishLoading:消息。
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(@"Succeeded! Receive %lu bytes of data(unsigned long)",[receivedData length]);
theConnection = nil;
receivedData = nil;
}
@end
發(fā)起一個POST請求
我們可以像發(fā)起其它URL請求一樣,發(fā)起一個HTTP或HTTPS POST請求。主要的區(qū)別在于我們必須先配置好NSMutableURLRequest對象,并將其作為參數(shù)傳遞給initWithRequest:delegate:方法。
另外,我們還需要構(gòu)造請求的body數(shù)據(jù)??梢砸韵旅嫒N方式來處理
對于上傳短小的內(nèi)存數(shù)據(jù),我們需要對已存在的數(shù)據(jù)塊進行URL編碼
如果是從磁盤中上傳文件,則調(diào)用setHTTPBodyStream:方法來告訴NSMutableURLRequest從一個NSInputStream中讀取并使用結(jié)果數(shù)據(jù)作為body的內(nèi)容
對于大塊的數(shù)據(jù),調(diào)用CFStreamCreateBoundPair來創(chuàng)建流對象對,然后調(diào)用setHTTPBodyStream:方法來告訴NSMutableURLRequest使用這些流對象中的一個作為body內(nèi)容的源。通過將數(shù)據(jù)寫入其它流,可以一次發(fā)送一小塊數(shù)據(jù)。
如果要上傳數(shù)據(jù)到一個兼容的服務(wù)器中,URL加載系統(tǒng)同樣支持100(繼續(xù))狀態(tài)碼,這樣允許一個上傳操作在發(fā)生認證錯誤或其它失敗時仍能繼續(xù)。為了開啟這個操作,可以設(shè)置請求對象的expect頭為100-continue。
代碼清單2展示了如何配置一個POST請求的NSMutableURLRequest對象
- (void)setRequestForPost
{
// 對于application/x-www-form-urlencoded類型的body數(shù)據(jù),form域的參數(shù)由&號分開,
NSString *bodyData = @"name=Jane+Doe&address=123+Main+St";
NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://www.apple.com"]];
// 設(shè)置content-type為application/x-www-form-urlencoded
[postRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
// 指定請求方法為POST
[postRequest setHTTPMethod:@"POST"];
[postRequest setHTTPBody:[NSData dataWithBytes:[bodyData UTF8String] length:strlen([bodyData UTF8String])]];
// Initialize the NSURLConnection and proceed as described in
// Retrieving the Contents of a URL
}
使用Block來接收數(shù)據(jù)
NSURLConnection類提供了類方法sendAsynchronousRequest:queue:completionHandler:,該方法可以以異常的方式向服務(wù)端發(fā)起請求,并在數(shù)據(jù)返回或發(fā)生錯誤/超時時調(diào)用block來處理。該方法需要一個請求對象,一個完成處理block,及block運行的隊列。當(dāng)請求完成或錯誤發(fā)生時,URL加載系統(tǒng)調(diào)用該block來處理結(jié)果數(shù)據(jù)或錯誤信息。
如果請求成功,則會傳遞一個NSData對象和一個NSURLResponse對象給block。如果失敗,則傳遞一個NSError對象。
這個方法有兩個限制
對于需要認證的請求,只提供最小的支持。
沒有辦法來修改響應(yīng)緩存和服務(wù)端重定向的默認行為