儘管NSURLConnection
已經不建議使用了,iOS 7
取而代之的是NSURLSession
,但是本文依然打算剖析一下,其中一些思想還是值得學習一下,如果您不用NSURLConnection
,並且也不打算嘗試使用,可以跳過本文。
一、概念介紹
NSURLConnection
是2003年隨着第一版Safari
的發佈而發佈的,它不單單是一個網絡請求類,而是指代Foundation
框架的URL
系統中的一系列關聯的組件:NSURLRequest
、NSURLResponse
、NSURLProtocol
、NSHTTPCookieStorage
、NSURLCredentialStorage
以及同名類NSURLConnection
。
1. 異步處理代理
其中,NSURLRequest
被傳遞給NSURLConnection
。被委託對象(遵守以前的非正式協議<NSURLConnectionDelegate>
和<NSURLConnectionDataDelegate>
)異步返回一個NSURLResponse
以及包含服務器返回信息的NSData
。
2. 請求策略
在一個請求被髮送到服務器之前,系統會先查詢共享的緩存信息,然後根據策略(policy
)以及可用性(availability
)的不同,一個已經被緩存的響應可能會被立即返回。如果沒有緩存的響應可用,則這個請求將根據我們指定的策略來緩存它的響應以便將來的請求可以使用。
3. 認證策略
在把請求發送給服務器的過程中,服務器可能會發出鑑權查詢(authentication challenge
),這可以由共享的 cookie
或機密存儲(credential storage
)來自動響應,或者由被委託對象來響應。發送中的請求也可以被註冊的NSURLProtocol
對象所攔截,以便在必要的時候無縫地改變其加載行爲。
4. 下載過程
NSURLConnection下載文件時,先是將整個文件下載到內存,然後再寫入到沙盒,如果文件比較大,就會出現內存暴漲的情況。
二、使用步驟
請求步驟:
- 設置請求路徑
- 創建請求對象(默認是GET請求,且已經默認包含了請求頭)
- 發送網絡請求
- 接收到服務器的響應後,解析響應體
0.常用代理介紹
/*
1.當接收到服務器響應的時候調用,該方法只會調用一次
第一個參數connection:監聽的是哪個NSURLConnection對象
第二個參數response:接收到的服務器返回的響應頭信息
*/
- (void)connection:(nonnull NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response
/*
2.當接收到數據的時候調用,該方法會被調用多次
第一個參數connection:監聽的是哪個NSURLConnection對象
第二個參數data:本次接收到的服務端返回的二進制數據(可能是片段)
*/
- (void)connection:(nonnull NSURLConnection *)connection didReceiveData:(nonnull NSData *)data
/*
3.當服務端返回的數據接收完畢之後會調用
通常在該方法中解析服務器返回的數據
*/
-(void)connectionDidFinishLoading:(nonnull NSURLConnection *)connection
/*4.當請求錯誤的時候調用(比如請求超時)
第一個參數connection:NSURLConnection對象
第二個參數:網絡請求的錯誤信息,如果請求失敗,則error有值
*/
- (void)connection:(nonnull NSURLConnection *)connection didFailWithError:(nonnull NSError *)error
1. 各種方法請求
- 方法一:利用
sendSynchronousRequest
發送GET
請求,該請求是同步的
//1.確定請求路徑
NSURL *url = [NSURL URLWithString:@""];
//2.創建一個請求對象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.把請求發送給服務器
//sendSynchronousRequest 阻塞式的方法,會卡住線程
NSHTTPURLResponse *response = nil;
NSError *error = nil;
/*
第一個參數:請求對象
第二個參數:響應頭信息,當該方法執行完畢之後,該參數被賦值
第三個參數:錯誤信息,如果請求失敗,則error有值
*/
//該方法是阻塞式的,會卡住線程
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
// 對data進行處理
- 方法二:利用
sendAsynchronousRequest
發送POST
請求,該請求是異步的
//1.確定請求路徑
NSURL *url = [NSURL URLWithString:@""];
//2.創建一個請求對象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 2.1設置請求方式
// 注意: POST一定要大寫
request.HTTPMethod = @"POST";
// 2.2設置請求體
// 注意: 如果是給POST請求傳遞參數: 那麼不需要寫?號
request.HTTPBody = [@"username=Mitchell&pwd=123456&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
//3.把請求發送給服務器,發送一個異步請求
/*
第一個參數:請求對象
第二個參數:回調方法在哪個線程中執行,如果是主隊列則block在主線程中執行,非主隊列則在子線程中執行
第三個參數: completionHandlerBlock塊:接受到響應的時候執行該block中的代碼
response:響應頭信息
data:響應體
connectionError:錯誤信息,如果請求失敗,那麼該參數有值
*/
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc]init] completionHandler:^(NSURLResponse * __nullable response, NSData * __nullable data, NSError * __nullable connectionError) {
//4.解析服務器返回的數據
NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
//轉換並打印響應頭信息
NSHTTPURLResponse *r = (NSHTTPURLResponse *)response;
}];
- 方法三:通過設置代理,來處理請求響應或數據
// 第一種代理方式,自動發送請求
[[NSURLConnection alloc]initWithRequest:request delegate:self];
/*
第一個參數:請求對象
第二個參數:誰成爲NSURLConnetion對象的代理
第三個參數:是否馬上發送網絡請求,如果該值爲YES則立刻發送,如果爲NO則不會發送網路請求
*/
NSURLConnection *conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
//在startImmediately爲NO時,調用該方法控制網絡請求的發送
[conn start];
// 第三種代理方式
//設置代理的第三種方式:使用類方法設置代理,會自動發送網絡請求
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
//取消網絡請求
//[conn cancel];
2. NSURLConnection 與 NSRunLoop 的關聯使用
主要是區分 NSURLConnection 在主線程和子線程發送網絡請求的區別
- 主線程
// 直接發送網絡請求,發送是異步的,但是代理方法是在主線程中執行的
NSURL *url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
NSURLRequest*request = [NSURLRequest requestWithURL:url];
//這裏分兩種方式發送請求
//2.1 直接發送網絡請求是異步的,但是回調方法是在主線程中執行的
//[[NSURLConnection alloc]initWithRequest:request delegate:self];
// 如果按照如下設置,那麼回調的代理方法也會運行在子線程中
NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
NSURLRequest*request = [NSURLRequest requestWithURL:url];
//2.2 設置回調方法也在子線程中運行
NSURLConnection*conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
[conn setDelegateQueue:[[NSOperationQueue alloc] init]];
[conn start];
- 子線程
因爲 NSURLConnection
是局部變量,當我們創建的時候其實是會默認添加到當前的 RunLoop
中,如果是在主線程添加,主線程的 RunLoop
是默認有的,無須我們創建;然而如果在子線程中,是默認沒有 RunLoop
和輸入源的,所以需要給子線程手動添加 RunLoop
。
調用start
方法時,如果沒有 RunLoop
,會默認添加一個 RunLoop
到當前的線程中來,然後將connection
加到runLoop
中。
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSRunLoop *loop = [NSRunLoop currentRunLoop];
[NSURLConnection connectionWithRequest:request delegate:self];
[loop run];
// 下面這樣無法發送請求
[NSURLConnection connectionWithRequest:request delegate:self];
});
至此,NSURLConnection
的簡介結束,只是提供了最簡單應用的參考,如有不足之處,請不吝指教。下一篇簡介目前推薦應用的NSURLSession
,敬請期待……
參考文檔
http://www.jianshu.com/p/056b1817d25a
http://www.jianshu.com/p/982a3035c93a