鍍金池/ 教程/ iOS/ iOS 7 的多任務
與四軸無人機的通訊
在沙盒中編寫腳本
結構體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學
NSString 與 Unicode
代碼簽名探析
測試
架構
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動開發(fā)
Collection View 動畫
截圖測試
MVVM 介紹
使 Mac 應用數(shù)據(jù)腳本化
一個完整的 Core Data 應用
插件
字符串
為 iOS 建立 Travis CI
先進的自動布局工具箱
動畫
為 iOS 7 重新設計 App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡應用實例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動畫解釋
響應式 Android 應用
初識 TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項目介紹
Swift 的強大之處
測試并發(fā)程序
Android 通知中心
調(diào)試:案例學習
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學習的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設計優(yōu)雅的移動游戲
繪制像素到屏幕上
相機與照片
音頻 API 一覽
交互式動畫
常見的后臺實踐
糟糕的測試
避免濫用單例
數(shù)據(jù)模型和模型對象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場
照片框架
響應式視圖
Square Register 中的擴張
DTrace
基礎集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設計的藝術
導航應用
線程安全類的設計
置換測試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機項目
Mach-O 可執(zhí)行文件
UI 測試
值對象
活動追蹤
依賴注入
Swift
項目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務
自定義 Collection View 布局
測試 View Controllers
訪談
收據(jù)驗證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場
游戲
調(diào)試核對清單
View Controller 容器
學無止境
XCTest 測試實戰(zhàn)
iOS 7
Layer 中自定義屬性的動畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲
代碼審查的藝術:Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴展
理解 Scroll Views
使用 VIPER 構建 iOS 應用
Android 中的 SQLite 數(shù)據(jù)庫支持
Fetch 請求
導入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機捕捉
語言標簽
同步案例學習
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識別
玩轉(zhuǎn)字符串
相機工作原理
Build 過程

iOS 7 的多任務

在 iOS 7 之前,當程序置于后臺之后開發(fā)者們對他們程序所能做的事情非常有限。除了 VOIP 和基于地理位置特性以外,唯一能做的地方就是使用后臺任務(background tasks)讓代碼可以執(zhí)行幾分鐘。如果你想下載比較大的視頻文件以便離線瀏覽,亦或者備份用戶的照片到你的服務器上,你都僅能完成一部分工作。

iOS 7 添加了兩個新的 API 以便你的程序可以在后臺更新界面以及內(nèi)容。首先是后臺獲?。˙ackground Fetch),它允許你定期地從網(wǎng)絡獲取新的內(nèi)容。第二個 API 就是遠程通知(Remote Notifications),這是一個當事件發(fā)生時可以讓推送通知主動提醒應用的新特性,這兩者都為你的應用界面保持最新提供了極大的幫助。在新的后臺傳輸服務 (Background Transfer Service) 中執(zhí)行定期的任務,也允許你在進程之外可以執(zhí)行網(wǎng)絡傳輸(下載和上傳)工作。

后臺獲取 (Background Fetch) 和遠程通知 (Remote Notification) 基于簡單的 ApplicationDelegate 鉤子,在應用程序掛起之前的 30 秒時鐘時間執(zhí)行工作。它們不是用于 CPU 頻繁工作或者長時間運行任務,而是用來處理長時間運行的網(wǎng)絡請求隊列,例如下載一部很大的電影,或者執(zhí)行快速的內(nèi)容更新。

對用戶來說,多任務處理有一點顯而易見的改變就是新的應用切換程序 (the new app switcher),它用來呈現(xiàn)應用到后臺時的界面快照。這些快照的存在是有一定理由的--現(xiàn)在你可以在后臺完成工作后更新程序快照,以用來呈現(xiàn)新的內(nèi)容。社交網(wǎng)絡、新聞或者天氣等應用現(xiàn)在都可以直接呈現(xiàn)最新的內(nèi)容而不需要用戶重新打開應用。我們稍后會介紹如何更新屏幕快照。

后臺獲取

后臺獲取是一種智能的輪詢機制,它很適合需要經(jīng)常更新內(nèi)容的程序,像社交網(wǎng)絡,新聞或天氣的程序。為了在用戶啟動程序前提前觸發(fā)后臺獲取,系統(tǒng)會根據(jù)用戶行為喚醒應用程序。舉個例子,如果用戶經(jīng)常在下午 1 點使用某個應用程序,系統(tǒng)會學習,適應并在使用周期前執(zhí)行后臺獲取。為了減少電池使用,使用設備無線通信的所有應用的后臺獲取會被合并,如果你向系統(tǒng)報告新數(shù)據(jù)無法獲取,iOS 會適應并使用此信息避免會繼續(xù)獲取。

開啟后臺獲取的第一步是在 info plist 文件中對 UIBackgroundModes 鍵指定特定的值。最簡單的途徑是在 Xcode 5 的 project editor 中新的 Capabilities 標簽頁中設置,這個標簽頁包含了后臺模式部分,可以方便配置多任務選項。

http://wiki.jikexueyuan.com/project/objc/images/5-12.jpg" alt="" />

或者,你可以手動編輯這個值

<key>UIBackgroundModes</key>
<array>
    <string>fetch</string>
</array>  

接下來,告訴 iOS 多久進行一次數(shù)據(jù)獲取

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];

    return YES;
}

iOS 默認不進行后臺獲取,所以你需要設置一個時間間隔,否則,你的應用程序永遠不能在后臺被喚醒。UIApplicationBackgroundFetchIntervalMinimum 這個值要求系統(tǒng)盡可能頻繁地去管理你的程序到底什么時候應該被喚醒,但如果你不需要這樣的話,你也應該指定一個你想要的的時間間隔。例如,一個天氣的應用程序,可能只需要幾個小時才更新一次,iOS 將會在后臺獲取之間至少等待你指定的時間間隔。

如果你的應用允許用戶退出登錄,那么就沒有獲取新數(shù)據(jù)的需要了,你應該把 minimumBackgroundFetchInterval 設置為 UIApplicationBackgroundFetchIntervalNever,這樣可以節(jié)省資源。

最后一步是在應用程序委托中實現(xiàn)下列方法:

- (void)                application:(UIApplication *)application 
  performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];

    NSURL *url = [[NSURL alloc] initWithString:@"http://yourserver.com/data.json"];
    NSURLSessionDataTask *task = [session dataTaskWithURL:url 
                                        completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

        if (error) {
            completionHandler(UIBackgroundFetchResultFailed);
            return;
        }

        // 解析響應/數(shù)據(jù)以決定新內(nèi)容是否可用
        BOOL hasNewData = ...
        if (hasNewData) {
            completionHandler(UIBackgroundFetchResultNewData);
        } else {
            completionHandler(UIBackgroundFetchResultNoData);
        }
    }];

    // 開始任務
    [task resume];
}

系統(tǒng)喚醒應用程序后將會執(zhí)行這個委托方法。需要注意的是,你只有 30 秒的時間來確定獲取的新內(nèi)容是否可用,然后處理新內(nèi)容并更新界面。30 秒時間應該足夠去從網(wǎng)絡獲取數(shù)據(jù)和獲取界面的縮略圖,但是最多只有 30 秒。當完成了網(wǎng)絡請求和更新界面后,你應該執(zhí)行完成的回調(diào)。

完成回調(diào)的執(zhí)行有兩個目的。首先,系統(tǒng)會估量你的進程消耗的電量,并根據(jù)你傳遞的 UIBackgroundFetchResult 參數(shù)記錄新數(shù)據(jù)是否可用。其次,當你調(diào)用完成的處理代碼時,應用的界面縮略圖會被采用,并更新應用程序切換器。當用戶在應用間切換時,用戶將會看到新內(nèi)容。這種通過 completion handler 來報告并且生成截圖的方法,在新的多任務處理 API 中是很常見的。

在實際應用中,你應該將 completionHandler 傳遞到應用程序的子組件,然后在處理完數(shù)據(jù)和更新界面后調(diào)用。

在這里,你可能想知道 iOS 是如何在應用程序后臺運行時獲得界面截圖的,并且想知道應用程序的生命周期與后臺獲取之間有什么關系。如果應用程序處于掛起狀態(tài),系統(tǒng)會先喚醒應用,然后再調(diào)用 application: performFetchWithCompletionHandler:。如果應用程序還沒有啟動,系統(tǒng)將會啟動它,然后調(diào)用常見的委托方法,包括 application: didFinishLaunchingWithOptions:。你可以把這種應用程序運行的方式想像為用戶從 Springboard 啟動這個程序,區(qū)別僅僅在于界面是看不見的,在屏幕外渲染的。

大多數(shù)情況下,無論應用在后臺啟動或者在前臺,你會執(zhí)行相同的工作,但你可以通過查看 UIApplication 的 applicationState 屬性來判斷應用是不是從后臺啟動。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSLog(@"Launched in background %d", UIApplicationStateBackground == application.applicationState);

    return YES;
}

測試后臺數(shù)據(jù)獲取

有兩種可以模擬后臺獲取的途徑。最簡單是從 Xcode 運行你的應用,當應用運行時,在 Xcode 的 Debug 菜單選擇 Simulate Background Fetch.

第二種方法,使用 scheme 更改 Xcode 運行程序的方式。在 Xcode 菜單的 Product 選項,選擇 Scheme 然后選擇 Manage Schemes。在這里,你可以編輯或者添加一個新的 scheme,然后選中 Launch due to a background fetch event 。如下圖:

http://wiki.jikexueyuan.com/project/objc/images/5-13.png" alt="" />

遠程通知

遠程通知允許你在重要事件發(fā)生時,告知你的應用。你可能需要發(fā)送新的即時信息,突發(fā)新聞的提醒,或者用戶喜愛電視的最新劇集已經(jīng)可以下載以便離線觀看的消息。遠程通知很適合用于那些偶爾出現(xiàn),但卻很重要的內(nèi)容,如果使用后臺獲取模式中在兩次獲取間需要等待的時間是不可接受的話,遠程通知會是一個不錯的選擇。遠程通知會比后臺獲取更有效率,因為應用程序只有在需要的時候才會啟動。

一條遠程通知實際上只是一條普通的帶有 content-available 標志的推送通知。你可以發(fā)送一條帶有提醒信息的推送去告訴用戶有事請發(fā)生了,同時在后臺對界面進行更新。但遠程通知也可以做到在安靜地,沒有提醒消息或者任何聲音的情況下,只去更新應用界面或者觸發(fā)后臺工作。然后你可以在完成下載或者處理完新內(nèi)容后,發(fā)送一條本地通知。

靜默的推送通知有速度限制,所以你可以大膽地根據(jù)應用程序的需要發(fā)送盡可能多的通知。iOS 和蘋果推送服務會控制推送通知多久被遞送,發(fā)送很多推送通知是沒有問題的。如果你的推送通知達到了限制,推送通知可能會被延遲,直到設備下次發(fā)送保持活動狀態(tài)的數(shù)據(jù)包,或者收到另外一個通知。

發(fā)送遠程通知

要發(fā)送一條遠程通知,需要在推送通知的有效負載(payload)設置 content-available 標志。content-available 標志和用來通知 報刊應用(Newsstand)的健值是一樣的,因此,大多數(shù)推送腳本和庫都已經(jīng)支持遠程通知。當你發(fā)送一條遠程通知時,你可能還想要包含一些通知有效負載中的數(shù)據(jù),讓你應用程序可以引用事件。這可以為你節(jié)省一些網(wǎng)絡請求,并提高應用程序的響應度。

我建議在開發(fā)的時候,使用 Nomad CLI’s Houston 工具發(fā)送推送消息,當然你也可以使用你喜歡的庫或腳本。

你可以通過 nomad-cli ruby gem 來安裝 Houston

gem install nomad-cli

然后通過包含在 Nomad 的 apn 實用工具發(fā)送一條通知:

# Send a Push Notification to your Device
apn push <device token> -c /path/to/key-cert.pem -n -d content-id=42

在這里,-n 標志指定應該包含 content-available 健值,-d 標志允許添加我們自定義的數(shù)據(jù)健值到有效負荷。

通知的有效負荷(payload)結果和下面類似:

{
    "aps" : {
        "content-available" : 1
    },
    "content-id" : 42
}

iOS 7 添加了一個新的應用程序委托方法,當接收到一條帶有 content-available 的推送通知時,下面的方法會被調(diào)用:

- (void)application:(UIApplication *)application 
  didReceiveRemoteNotification:(NSDictionary *)userInfo 
        fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    NSLog(@"Remote Notification userInfo is %@", userInfo);

    NSNumber *contentID = userInfo[@"content-id"];
    // 根據(jù) content ID 進行操作
    completionHandler(UIBackgroundFetchResultNewData);
}

和后臺抓取一樣,應用程序進入后臺啟動,也有 30 秒的時間去獲取新內(nèi)容并更新界面,最后調(diào)用完成的處理代碼。我們可以像后臺獲取那樣,執(zhí)行快速的網(wǎng)絡請求,但我們可以使用新的強大的后臺傳輸服務,處理任務隊列,下面看看我們?nèi)绾卧谌蝿胀瓿珊蟾陆缑妗?/p>

NSURLSession 和 后臺傳輸服務(Background Transfer Service)

NSURLSession 是 iOS 7 添加的一個新類,它也是 Foundation networking 中的新技術。作為 NSURLConnection 的替代品,一些熟悉的概念和類都保留下來了,例如 NSURL,NSURLRequestNSURLResponse。所以,你可以使用 NSURLSessionTask 這一 NSURLConnection 的替代品,來處理網(wǎng)絡請求及響應。一共有 3 種會話任務:數(shù)據(jù),下載和上傳。每一種都向 NSURLSessionTask 添加了語法糖,根據(jù)你的需要,適當選擇一種。

一個 NSURLSession 對象協(xié)調(diào)一個或多個 NSURLSessionTask 對象,并根據(jù) NSURLSessionTask 創(chuàng)建的 NSURLSessionConfiguration 實現(xiàn)不同的功能。使用相同的配置,你也可以創(chuàng)建多組具有相關任務的 NSURLSession 對象。要利用后臺傳輸服務,你將會使用 [NSURLSessionConfiguration backgroundSessionConfiguration] 來創(chuàng)建一個會話配置。添加到后臺會話的任務在外部進程運行,即使應用程序被掛起,崩潰,或者被殺死,它依然會運行。

NSURLSessionConfiguration 允許你設置默認的 HTTP 頭,配置緩存策略,限制使用蜂窩數(shù)據(jù)等等。其中一個選項是 discretionary 標志,這個標志允許系統(tǒng)為分配任務進行性能優(yōu)化。這意味著只有當設備有足夠電量時,設備才通過 Wifi 進行數(shù)據(jù)傳輸。如果電量低,或者只僅有一個蜂窩連接,傳輸任務是不會運行的。后臺傳輸總是在 discretionary 模式下運行。

目前為止,我們大概了解了 NSURLSession,以及一個后臺會話如何進行,接下來,讓我們回到遠程通知的例子,添加一些代碼來處理后臺傳輸服務的下載隊列。當下載完成后,我們會通知用戶該文件已經(jīng)可以使用了。

NSURLSessionDownloadTask

首先,我們先處理一條遠程通知,并把一個 NSURLSessionDownloadTask 添加到后臺傳輸服務的隊列。在 backgroundURLSession 方法中,我們根據(jù)后臺會話配置,創(chuàng)建一個 NSURLSession 對象,并把 application delegate 作為會話的委托對象。文檔不建議對于相同的標識符 (identifier) 創(chuàng)建多個會話對象,所以我們使用 dispatch_once 來避免潛在的問題:

- (NSURLSession *)backgroundURLSession
{
    static NSURLSession *session = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSString *identifier = @"io.objc.backgroundTransferExample";
        NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration backgroundSessionConfiguration:identifier];
        session = [NSURLSession sessionWithConfiguration:sessionConfig 
                                                delegate:self 
                                           delegateQueue:[NSOperationQueue mainQueue]];
    });

    return session;
}

- (void)           application:(UIApplication *)application 
  didReceiveRemoteNotification:(NSDictionary *)userInfo 
        fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    NSLog(@"Received remote notification with userInfo %@", userInfo);

    NSNumber *contentID = userInfo[@"content-id"];
    NSString *downloadURLString = [NSString stringWithFormat:@"http://yourserver.com/downloads/%d.mp3", [contentID intValue]];
    NSURL* downloadURL = [NSURL URLWithString:downloadURLString];

    NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL];
    NSURLSessionDownloadTask *task = [[self backgroundURLSession] downloadTaskWithRequest:request];
    task.taskDescription = [NSString stringWithFormat:@"Podcast Episode %d", [contentID intValue]];
    [task resume];

    completionHandler(UIBackgroundFetchResultNewData);
}

我們使用 NSURLSession 類方法創(chuàng)建一個下載任務,配置請求,并提供說明供以后使用。因為所有會話任務一開始處于掛起狀態(tài),你必須謹記要調(diào)用 [task resume] 保證開始了任務。

現(xiàn)在,我們需要實現(xiàn) NSURLSessionDownloadDelegate 的委托方法,當下載完成時,調(diào)用回調(diào)函數(shù)。如果你需要處理認證或會話生命周期的其他事件,你可能還需要實現(xiàn) NSURLSessionDelegateNSURLSessionTaskDelegate 的方法。你應該閱讀 Apple 的 Life Cycle of a URL Session with Custom Delegates 文檔,它講解了所有類型的會話任務的完整生命周期。

NSURLSessionDownloadDelegate 中的委托方法全部是必須實現(xiàn)的,盡管在這個例子中我們只需要用到 [NSURLSession downloadTask:didFinishDownloadingToURL:]。任務完成下載時,你會得到一個磁盤上該文件的臨時 URL。你必須把這個文件移動或復制你的應用程序空間,因為當你從這個委托方法返回時,該文件將從臨時存儲中刪除。

#Pragma Mark - NSURLSessionDownloadDelegate

- (void)         URLSession:(NSURLSession *)session 
               downloadTask:(NSURLSessionDownloadTask *)downloadTask
  didFinishDownloadingToURL:(NSURL *)location
{
    NSLog(@"downloadTask:%@ didFinishDownloadingToURL:%@", downloadTask.taskDescription, location);

    // 用 NSFileManager 將文件復制到應用的存儲中
    // ...

    // 通知 UI 刷新
}

- (void)  URLSession:(NSURLSession *)session 
        downloadTask:(NSURLSessionDownloadTask *)downloadTask 
   didResumeAtOffset:(int64_t)fileOffset 
  expectedTotalBytes:(int64_t)expectedTotalBytes
{
}

- (void)         URLSession:(NSURLSession *)session 
               downloadTask:(NSURLSessionDownloadTask *)downloadTask 
               didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten 
  totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
}

當后臺會話任務完成時,如果你的應用程序仍然在前臺運行,上面的代碼已經(jīng)足夠了。然而,在大多數(shù)情況下,你的應用程序可能是沒有運行的,或者在后臺被掛起。在這些情況下,你必須實現(xiàn)應用程序委托的兩個方法,這樣系統(tǒng)就可以喚醒你的應用程序。不同于以往的委托回調(diào),該應用程序委托會被調(diào)用兩次,因為您的會話和任務委托可能會收到一系列消息。app delegate 的:handleEventsForBackgroundURLSession: 方法會在這些 NSURLSession 委托的消息發(fā)送前被調(diào)用,然后,URLSessionDidFinishEventsForBackgroundURLSession 在隨后被調(diào)用。在前面的方法中,包含了一個后臺完成的回調(diào)(completionHandler),并在后面的方法中執(zhí)行回調(diào)以便更新界面:

- (void)                  application:(UIApplication *)application 
  handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler
{
    // 你必須重新建立一個后臺 seesiong 的參照
    // 否則 NSURLSessionDownloadDelegate 和 NSURLSessionDelegate 方法會因為
    // 沒有 對 session 的 delegate 設定而不會被調(diào)用。參見上面的 backgroundURLSession
    NSURLSession *backgroundSession = [self backgroundURLSession];

    NSLog(@"Rejoining session with identifier %@ %@", identifier, backgroundSession);

    // 保存 completion handler 以在處理 session 事件后更新 UI
    [self addCompletionHandler:completionHandler forSession:identifier];
}

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
    NSLog(@"Background URL session %@ finished events.\n", session);

    if (session.configuration.identifier) {
        // 調(diào)用在 -application:handleEventsForBackgroundURLSession: 中保存的 handler
        [self callCompletionHandlerForSession:session.configuration.identifier];
    }
}

- (void)addCompletionHandler:(CompletionHandlerType)handler forSession:(NSString *)identifier
{
    if ([self.completionHandlerDictionary objectForKey:identifier]) {
        NSLog(@"Error: Got multiple handlers for a single session identifier.  This should not happen.\n");
    }

    [self.completionHandlerDictionary setObject:handler forKey:identifier];
}

- (void)callCompletionHandlerForSession: (NSString *)identifier
{
    CompletionHandlerType handler = [self.completionHandlerDictionary objectForKey: identifier];

    if (handler) {
        [self.completionHandlerDictionary removeObjectForKey: identifier];
        NSLog(@"Calling completion handler for session %@", identifier);

        handler();
    }
}

如果當后臺傳輸完成時,應用程序不再停留在前臺,那么,對于更新程序界面來說,這個兩步處理過程是必要的。此外,如果當后臺傳輸完成時,應用程序根本沒有在運行,iOS 將會在后臺啟動該應用程序,然后前面的應用程序和會話的委托方法會在 application:didFinishLaunchingWithOptions: 方法被調(diào)用之后被調(diào)用。

配置和限制

我們簡單地體驗了后臺傳輸?shù)膹姶笾帲銘撋钊胛臋n,閱讀 NSURLSessionConfiguration 部分,以便最好地滿足你的使用場景。例如,NSURLSessionTasks 通過 NSURLSessionConfigurationtimeoutIntervalForResource 屬性,支持資源超時特性。你可以使用這個特性指定你允許完成一個傳輸所需的最長時間。內(nèi)容只在有限的時間可用,或者在用戶只有有限 Wifi 帶寬的時間內(nèi)無法下載或上傳資源的情況下,你也可以使用這個特性。

除了下載任務,NSURLSession 也全面支持上傳任務,因此,你可能會在后臺將視頻上傳到服務器,這保證用戶不需要再像 iOS 6 那樣保持應用程序前臺運行。如果當傳輸完成時你的應用程序不需要在后臺運行,一個比較好的做法是,把 NSURLSessionConfigurationsessionSendsLaunchEvents 屬性設置為 NO。高效利用系統(tǒng)資源,是一件讓 iOS 和用戶都高興的事。

最后,我們來說一說使用后臺會話的幾個限制。作為一個必須實現(xiàn)的委托,您不能對 NSURLSession 使用簡單的基于 block 的回調(diào)方法。后臺啟動應用程序,是相對耗費較多資源的,所以總是采用 HTTP 重定向。后臺傳輸服務只支持 HTTP 和 HTTPS,你不能使用自定義的協(xié)議。系統(tǒng)會根據(jù)可用的資源進行優(yōu)化,在任何時候你都不能強制傳輸任務在后臺進行。

另外,要注意的是在后臺會話中,NSURLSessionDataTasks 是完全不支持的,你應該只出于短期的,小請求為目的使用這些任務,而不是用來下載或上傳。

總結

iOS 7 中強大的多任務和網(wǎng)絡 API 為現(xiàn)有應用和新應用開啟了一系列全新的可能性。如果你的應用程序可以從進程外的網(wǎng)絡傳輸和數(shù)據(jù)中獲益,那么盡情地使用這些美妙的 API。一般情況下,你可以就像你的應用正在前臺運行那樣去實現(xiàn)后臺傳輸,并進行適當?shù)慕缑娓?,而這里絕大多數(shù)的工作都已經(jīng)為你完成了。

  • 使用適當?shù)男?API 來為你的應用程序提供內(nèi)容。
  • 盡可能早地調(diào)用 completion handler 以提高效率。
  • 讓 completion handler 為應用程序更新界面快照。

擴展閱讀

上一篇:Core Data 概述下一篇:調(diào)試