從 NSURLConnection 到 NSURLSession
前言
現如今的移動應用開發,網路模組幾乎成了標配。如果你是早期 iOS 開發者的話,那麼你對 NSURLConnection
一定不會陌生。但其操作起來有許多不便,這也使得大家更願意使用第三方庫的解決方案,比如大名鼎鼎的AFNetworking 你一定有所耳聞。正是因為這一點,蘋果隨著 iOS 7 的釋出,也為開發者帶來了改進後的原生網路庫支援,那就是 NSURLSession。
今天,就讓我來給你道一道從 NSURLConnection
到 NSURLSession
那些你知道和不知道的事。
NSURLConnection
NSURLConnection
作為 Core Foundation / CFNetwork 框架的 API 之上的一個抽象,在 2003 年,隨著第一版的 Safari 的釋出就釋出了。NSURLConnection
這個名字,實際上是指代的 Foundation 框架的 URL 載入系統中一系列相關聯的元件:NSURLRequest
、NSURLResponse
、NSURLProtocol
、 NSURLCache
、 NSHTTPCookieStorage
、NSURLCredentialStorage
以及同名類 NSURLConnection
NSURLRequest
被傳遞給 NSURLConnection
。被委託物件(遵守以前的非正式協議 <NSURLConnectionDelegate>
和 <NSURLConnectionDataDelegate>
)非同步地返回一個 NSURLResponse
以及包含伺服器返回資訊的 NSData
。
在一個請求被髮送到伺服器之前,系統會先查詢共享的快取資訊,然後根據策略(policy)以及可用性(availability)的不同,一個已經被快取的響應可能會被立即返回。如果沒有快取的響應可用,則這個請求將根據我們指定的策略來快取它的響應以便將來的請求可以使用。
在把請求傳送給伺服器的過程中,伺服器可能會發出鑑權查詢(authentication challenge),這可以由共享的 cookie 或機密儲存(credential storage)來自動響應,或者由被委託物件來響應。傳送中的請求也可以被註冊的NSURLProtocol
物件所攔截,以便在必要的時候無縫地改變其載入行為。
使用步驟
概覽
(圖片來自)
NSURL
建立一個 NSURL
物件,設定請求路徑:
|
|
解釋如下:
- 協議:不同的協議,代表著不同的資源查詢方式、資源傳輸方式,比如常用的 HTTP、FTP 等
- 主機地址:存放資源的主機的 IP 地址(域名)
- 路徑:資源在主機中的具體位置
- 引數:引數可有可無,也可以多個。如果帶引數的話,用 “?” 號後面接引數,多個引數的話之間用 “&” 隔開
NSURLRequest
建立一個 NSURLRequest
物件並傳入 NSURL
,設定請求頭和請求體:
|
|
引數解釋如下:
- requestWithURL:資源路徑
- cachePolicy:快取策略(無論使用哪種快取策略,都會在本地快取資料),型別為列舉型別,取值如下:
- NSURLRequestUseProtocolCachePolicy = 0 // 預設的快取策略,使用協議的快取策略
- NSURLRequestReloadIgnoringLocalCacheData = 1 // 每次都從網路載入
- NSURLRequestReturnCacheDataElseLoad = 2 // 返回快取否則載入,很少使用
- NSURLRequestReturnCacheDataDontLoad = 3 // 只返回快取,沒有也不載入,很少使用
- timeoutInterval:超時時長,預設 60s
另外,還可以設定其它一些資訊,比如請求頭,請求體等等,如下:
|
|
注意,上面的 request 應為 NSMutableURLRequest
,即可變型別。(使用 POST 請求的話,那麼就必須的使用NSMutableURLRequest
)
NSURLConnection
使用 NSURLConnection
傳送請求,通過返回 NSURLResponse
例項和 NSError
例項分析結果,接受伺服器返回資料。
NSURLConnection
預設的請求型別為 GET,下面分非同步和同步介紹 GET 的使用(POST 會在 NSURLSession
中介紹)。
非同步請求
非同步是指:在傳送請求之後,一邊在子執行緒中接收返回資料,一邊執行之後的程式碼,當返回資料接收完畢後,採用回撥的方式通知主執行緒做處理。同時,非同步請求有兩種實現方式。
1.使用 block:
|
|
引數說明如下:
- completionHandler:請求響應後(或者請求超時)執行的程式碼,queue 為程式碼新增到的佇列,即 block執行的執行緒
- NSURLResponse 為伺服器的響應,真實型別為 NSHTTPURLResponse,通常只在「下載」功能時,才會使用;下面是協議頭的引數:
- URL:響應的 URL,有的時候,訪問一個 URL 地址,伺服器可能會出現重定向,會定位到新的地址
- MIMEType(Content-Type):伺服器告訴客戶端,可以用什麼軟體開啟二進位制資料。網路之所以豐富多采,是因為有豐富的客戶端軟體。栗子:Windows 上提示安裝 Flash 外掛
- expectedContentLength:預期的內容長度,要下載的檔案長度,下載檔案時非常有用
- suggestedFilename:「建議」的檔名,方便使用者直接儲存,很多時候,使用者並不關心要儲存成什麼名字
- textEncodingName:文字的編碼名稱 @”UTF8”,大多數都是 UTF8
- statusCode:狀態碼,在做下載操作的時候,需要判斷一下
- allHeaderFields:所有的響應頭字典時候,使用者並不關心要儲存成什麼名字
- NSURLResponse 為伺服器的響應,真實型別為 NSHTTPURLResponse,通常只在「下載」功能時,才會使用;下面是協議頭的引數:
- NSData:伺服器返回的資料,例如:JSON、XML
- NSError:網路訪問錯誤碼
2.使用代理(Delegate):
|
|
|
|
使用代理可以監測下載過程:
|
|
同步
同步是指:資料的請求在主執行緒來執行,一旦傳送同步請求,直至伺服器返回資料完成,才可以進行下一步操作,而網路資料載入需要一個時間過程,這樣的話就會堵塞主執行緒,當然這種使用場景很少。
|
|
NSURLSession
不管怎樣,NSURLConnection
作為網路基礎架構,已經服務了成千上萬的 iOS 和 Mac OS 程式,並且做的還算相當不錯。但是這些年,一些用例——尤其是在 iPhone 和 iPad 上面——已經對 NSURLConnection
的幾個核心概念提出了挑戰,讓蘋果有理由對它進行重構。
在 2013 的 WWDC 上,蘋果推出了 NSURLConnection
的繼任者:NSURLSession
。
和 NSURLConnection
一樣,NSURLSession
指的也不僅是同名類 NSURLSession
,還包括一系列相互關聯的類。NSURLSession
包括了與之前相同的元件,NSURLRequest
與 NSURLCache
,但是把 NSURLConnection
替換成了 NSURLSession
、NSURLSessionConfiguration
以及 NSURLSessionTask
的 3 個子類:NSURLSessionDataTask
,NSURLSessionUploadTask
,NSURLSessionDownloadTask
。
與 NSURLConnection
相比,NSURLsession
最直接的改進就是可以配置每個 session 的快取,協議,cookie,以及證書策略(credential policy),甚至跨程式共享這些資訊。這將允許程式和網路基礎框架之間相互獨立,不會發生干擾。每個 NSURLSession
物件都由一個 NSURLSessionConfiguration
物件來進行初始化,後者指定了剛才提到的那些策略以及一些用來增強移動裝置上效能的新選項。
NSURLSession
中另一大塊就是 session task。它負責處理資料的載入以及檔案和資料在客戶端與服務端之間的上傳和下載。NSURLSessionTask
與 NSURLConnection
最大的相似之處在於它也負責資料的載入,最大的不同之處在於所有的 task 共享其創造者 NSURLSession
這一公共委託者(common delegate)。
我們先來深入探討 task,過後再來討論 NSURLSessionConfiguration
。
概覽
(圖片來自)
Sessions
|
|
NSURLSessionTask
NSURLSession
本身是不會進行請求的,而是通過建立 task 的形式進行網路請求,同一個 NSURLSession 可以建立多個 task,並且這些 task 之間的 cache 和 cookie 是共享的。那麼我們就來看看 NSURLSession 都能建立哪些 task 吧。
Task 可以翻譯為任務,那麼在和網路請求相關的任務中,我們可以理解為:資料請求任務、下載任務、上傳任務等。
其實從類名就能猜出它們各自的用途:
-
NSURLSessionDataTask
:使用這個 task 來呼叫 HTTP GET 方式請求,從伺服器獲取資料到記憶體。返回的資料格式是 NSData,可根據需要自行轉換成 XML、JSON 等資料格式 -
NSURLSessionUploadTask
:使用這個 task 來上傳磁碟檔案到 web 伺服器,典型地通過 HTTP POST 或者 PUT 方式 -
NSURLSessionDownloadTask
:使用這個 task 來從遠端伺服器下載檔案到臨時檔案地址 -
NSURLSessionStreamTask
:使用這個 task 來執行非同步的讀和寫
你也能暫停,恢復(開始)和取消 tasks。
NSURLSessionDataTask POST
|
|
NSURLSessionDataDelegate 代理方法
NSURLSession
提供了 block 的方式處理返回的資料,但是如果我們想要在接收資料的過程中處理資料,我們可以使用 NSURLSessionDataDelegate
的代理方法,分為接收響應、接收資料和請求完成三個階段。
|
|
NSURLSessionConfiguration
NSURLSessionConfiguration
物件用於對 NSURLSession
物件進行初始化。從指定可用網路,到 cookie,安全性,快取策略,再到使用自定義協議,啟動事件的設定,以及用於移動裝置優化的幾個新屬性,你會發現使用NSURLSessionConfiguration
可以找到幾乎任何你想要進行配置的選項。其有三個類工廠方法:
-
預設模式(+defaultSessionConfiguration):返回一個標準的 configuration,工作模式類似於 NSURLConnection,可以使用快取的 Cache,Cookie,證書(credential)
-
及時模式(+ephemeralSessionConfiguration): 臨時 session 配置,與預設配置相比,這個配置不會使用快取的 Cache,Cookie,證書,只會存在記憶體裡,所以當程式退出時,所有的資料都會消失,這對於實現像祕密瀏覽這種功能來說是很理想的
-
後臺模式(+backgroundSessionConfiguration):它會建立一個後臺 session。後臺 session 不同於常規的,普通的 session,它甚至可以在應用程式掛起,退出或者崩潰的情況下執行上傳和下載任務。初始化時指定的識別符號,被用於向任何可能在程序外恢復後臺傳輸的守護程序(daemon)提供上下文
除了這三種預設的模式之外 NSURLSessionConfiguration
還可以進行很多的配置。 timeoutIntervalForRequest
和 timeoutIntervalForResource
可以控制網路操作的超時時間。 allowsCellularAccess
屬性可以控制是否允許使用無線網路。HTTPAdditionalHeaders
可以指定 HTTP 請求頭。
NSURLSessionConfiguration
幾乎可以完成網路操作的大多數配置功能,並且這些配置都繫結到當前的 Session 中,我們一旦用配置好的 NSURLSessionConfiguration
初始化 NSURLSession
例項後,就不能修改這個 NSURLSession
相關的配置了。所以,一切的配置操作都放在初始化 NSURLSession
之前。
iOS 9
-
在 iOS 7 後,蘋果推薦使用
NSURLSession
,並在 iOS 9 中廢棄了NSURLConnection
-
iOS 9 所有網路連線預設為 HTTPS 服務,為此推出 App Transport Security,詳情可參見:Networking with NSURLSession
NSURLConnection vs. NSURLSession
-
後臺上傳和下載:只需在建立
NSURLSession
的時候配置一個選項,就能得到後臺網路的所有好處。這樣可以延長電池壽命,並且還支援 UIKit 的多 task,在程序間使用相同的委託模型 -
能夠暫停和恢復網路操作:使用 NSURLSession API 能夠暫停,停止,恢復所有的網路任務,再也完全不需要子類化 NSOperation
-
可配置的容器:對於
NSURLSession
裡面的 requests 來說,每個NSURLSession
都是可配置的容器。舉個例來說,假如你需要設定 HTTP header 選項,你只用做一次,session 裡面的每個 request 就會有同樣的配置 -
提高認證處理:認證是在一個指定的連線基礎上完成的。在使用
NSURLConnection
時,如果發出一個訪問,會返回一個任意的 request。此時,你就不能確切的知道哪個 request 收到了訪問。而在 NSURLSession 中,就能用代理處理認證 -
豐富的代理模式:在處理認證的時候,
NSURLConnection
有一些基於非同步的 block 方法,但是它的代理方法就不能處理認證,不管請求是成功或是失敗。在NSURLSession
中,可以混合使用代理和 block 方法處理認證 -
上傳和下載通過檔案系統:它鼓勵將資料(檔案內容)從元資料(URL 和 settings)中分離出來
要點
-
請求的快取策略使用
NSURLRequestReloadIgnoringCacheData
,忽略本地快取 -
伺服器響應結束後,要記錄 Etag,伺服器內容和本地快取對比是否變化的重要依據
-
在傳送請求時,設定 If-None-Match,並且傳入 etag
-
連線結束後,要判斷響應頭的狀態碼,如果是 304,說明本地快取內容沒有發生變化,此時可以使用本地快取來載入資料,如果快取檔案被意外刪除,程式依然執行但會報錯,載入資料也失敗
-
GET 快取的資料會儲存在 Cache 目錄中 /bundleId 下,Cache.db 中
原文連結:http://itangqi.me/2016/04/01/from-nsurlconnection-to-nsurlsession/