NSURLSession是iOS7中新的網絡接口,它與咱們熟悉的NSURLConnection是並列的。在程序在前臺時,NSURLSession與NSURLConnection可以互爲替代工作。注意,如果用戶強制將程序關閉,NSURLSession會斷掉。
NSURLSession提供的功能:
1.通過URL將數據下載到內存
2.通過URL將數據下載到文件系統
3.將數據上傳到指定URL
4.在後臺完成上述功能
工作流程
如果我們需要利用NSURLSession進行數據傳輸我們需要:
1.創建一個NSURLSessionConfiguration,用於第二步創建NSSession時設置工作模式和網絡設置:
工作模式分爲:
一般模式(default):工作模式類似於原來的NSURLConnection,可以使用緩存的Cache,Cookie,鑑權。
及時模式(ephemeral):不使用緩存的Cache,Cookie,鑑權。
後臺模式(background):在後臺完成上傳下載,創建Configuration對象的時候需要給一個NSString的ID用於追蹤完成工作的Session是哪一個(後面會講到)。
網絡設置:參考NSURLConnection中的設置項。
1. 創建一個NSURLSession,系統提供了兩個創建方法:
sessionWithConfiguration:
sessionWithConfiguration:delegate:delegateQueue:
第一個粒度較低就是根據剛纔創建的Configuration創建一個Session,系統默認創建一個新的OperationQueue處理Session的消息。
第二個粒度比較高,可以設定回調的delegate(注意這個回調delegate會被強引用),並且可以設定delegate在哪個OperationQueue回調,如果我們將其設置爲[NSOperationQueue mainQueue]就能在主線程進行回調非常的方便。
2.創建一個NSURLRequest調用剛纔的NSURLSession對象提供的Task函數,創建一個NSURLSessionTask。
根據職能不同Task有三種子類:
NSURLSessionUploadTask:上傳用的Task,傳完以後不會再下載返回結果;
NSURLSessionDownloadTask:下載用的Task;
NSURLSessionDataTask:可以上傳內容,上傳完成後再進行下載。
得到的Task,調用resume開始工作。
3.如果是細粒度的Session調用,Session與Delegate會在指定的OperationQueue中進行交互,以咱們下載例子,交互過程的順序圖如下(假如不需要鑑權,即非HTTPS請求):
4.當不再需要連接調用Session的invalidateAndCancel直接關閉,或者調用finishTasksAndInvalidate等待當前Task結束後關閉。這時Delegate會收到URLSession:didBecomeInvalidWithError:這個事件。Delegate收到這個事件之後會被解引用。
5.如果是一個BackgroundSession,在Task執行的時候,用戶切到後臺,Session會和ApplicationDelegate做交互。當程序切到後臺後,在BackgroundSession中的Task還會繼續下載,這部分文檔敘述比較少,現在分三個場景分析下Session和Application的關係:
1)當加入了多個Task,程序沒有切換到後臺。
這種情況Task會按照NSURLSessionConfiguration的設置正常下載,不會和ApplicationDelegate有交互。
2)當加入了多個Task,程序切到後臺,所有Task都完成下載。
在切到後臺之後,Session的Delegate不會再收到,Task相關的消息,直到所有Task全都完成後,系統會調用ApplicationDelegate的application:handleEventsForBackgroundURLSession:completionHandler:回調,之後“彙報”下載工作,對於每一個後臺下載的Task調用Session的Delegate中的URLSession:downloadTask:didFinishDownloadingToURL:(成功的話)和URLSession:task:didCompleteWithError:(成功或者失敗都會調用)。
之後調用Session的Delegate回調URLSessionDidFinishEventsForBackgroundURLSession:。
注意:在ApplicationDelegate被喚醒後,會有個參數ComplietionHandler,這個參數是個Block,這個參數要在後面Session的Delegate中didFinish的時候調用一下,如下:
- @implementation APLAppDelegate
-
-
-
- - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier
-
- completionHandler:(void (^)())completionHandler
-
- {
-
- BLog();
-
-
-
-
-
-
-
- self.backgroundSessionCompletionHandler = completionHandler;
-
- }
-
-
-
- @end
-
-
-
-
-
- @implementation APLViewController
-
-
-
- - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
-
- {
-
- APLAppDelegate *appDelegate = (APLAppDelegate *)[[UIApplication sharedApplication] delegate];
-
- if (appDelegate.backgroundSessionCompletionHandler) {
-
- void (^completionHandler)() = appDelegate.backgroundSessionCompletionHandler;
-
- appDelegate.backgroundSessionCompletionHandler = nil;
-
- completionHandler();
-
- }
-
-
-
- NSLog(@"All tasks are finished");
-
- }
-
- @end
3)當加入了多個Task,程序切到後臺,下載完成了幾個Task,然後用戶又切換到前臺。(程序沒有退出)
切到後臺之後,Session的Delegate仍然收不到消息。在下載完成幾個Task之後再切換到前臺,系統會先彙報已經下載完成的Task的情況,然後繼續下載沒有下載完成的Task,後面的過程同第一種情況。
4)當加入了多個Task,程序切到後臺,幾個Task已經完成,但還有Task還沒有下載完的時候關掉強制退出程序,然後再進入程序的時候。(程序退出了)
最後這個情況比較有意思,由於程序已經退出了,後面沒有下完Session就不在了後面的Task肯定是失敗了。但是已經下載成功的那些Task,新啓動的程序也沒有聽“彙報”的機會了。經過實驗發現,這個時候之前在NSURLSessionConfiguration設置的NSString類型的ID起作用了,當ID相同的時候,一旦生成Session對象並設置Delegate,馬上可以收到上一次關閉程序之前沒有彙報工作的Task的結束情況(成功或者失敗)。但是當ID不相同,這些情況就收不到了,因此爲了不讓自己的消息被別的應用程序收到,或者收到別的應用程序的消息,起見ID還是和程序的Bundle名稱綁定上比較好,至少保證唯一性。
總結
就像前面說的,在普通的應用場景下NSURLSession與NSURLConnection相比沒有什麼優勢,但是在程序切換到後臺之後Background的Session就顯得更加靈活了。
另外,現在主流的網絡開發框架AFNetworking已經更新到了2.0(只支持iOS 6 / iOS 7),其中最重要的一個更新就是添加了NSURLSession相關的支持。雖然就我現在(2013.10.13)看到他們的源碼中,還沒有完全的支持後臺的Session(或者說沒有考慮全我上述的後臺情況),但是大家有興趣可以關注一下他們後續的更新情況。
參考資料:
注意:蘋果又開始不更新Xcode 中的文檔,然後悄悄在網上更新了,大家請關注相關文檔最後的更新時間,以最新的爲準