1. 程式人生 > >Ios和tvos按需請求資源簡介

Ios和tvos按需請求資源簡介

戴維營教育原創文章,轉載請註明出處。我們的夢想是做最好的iOS開發培訓!

介紹

與iOS 9和watchOS 2一起,蘋果引入了一套新的內容分發API,以便節約裝置空間,這就是按需載入資源。通過使用按需載入資源,我們可以將特定的應用程式資源託管在蘋果的伺服器上,然後在需要的時候進行載入。在這個教程中,我將通過開發一個圖片檢視應用介紹一下按需載入資源的基本用法。

準備工作

這個教程需要使用Xcode 7+並且熟悉iOS開發。可以到Github上下載初始專案。

1. 按需載入資源

益處

iOS 9和watchOS 2引入按需載入資源的主要目的是減少單個應用程式所佔的空間。另外一個好處是我們的應用程式能夠更快的被使用者下載下來並且啟動。

通過使用Xcode中的asset pack所唯一分配的tags來獲取按需載入資源。這些資源包可用包含任何asset中的內容(圖片、SpriteKit紋理、資料等),甚至其它檔案如OpenGL和Metal的著色器、SpriteKit和SceneKit的場景檔案以及粒子系統。

當我們將應用提交到App Store時,這些資源也會被上傳和託管,以便需要的時候下載。程式執行的時候,只要使用在Xcode中設定的tag就可以非常方便的獲取到資源。

類別

使用按需載入資源最多的兩個方面是app bundle,包含可執行程式碼和重要的資源,例如UI圖示和asset packs

在Xcode中可以將這些資源分為三個主要的類別:

  • 初始安裝: 第一次執行需要的內容,過後可以刪除。一般可以包含遊戲的開始幾關,當遊戲進度足夠遠的時候就可以刪除了。
  • 預取: 這個類別的內容在安裝完應用後就需要立即下載。這個型別的資料應該應用在那些非必需的資源上,但是如果安裝了可以獲取更好的使用者體驗。比如遊戲的新手教程。
  • 按需載入: 在程式執行過程中下載,不會影響程式的執行。這是我們使用最多的按需載入型別。
限制

支援按需載入資源的應用程式需要遵循以下限制:

  • iOS應用程式不能超過2GB
  • 初始安裝的tag不能超過2GB
  • 預取tag不能超過2GB
  • 正在使用的資源不能超過2GB。這一點只有當應用正在執行並且使用了按需載入資源的時候才起作用。
  • 每個asset pack不能給你超過512MB。如果超過了這個限制,Xcode將給出警告並且允許我們繼續測試和開發程式,但是提交到App Store時會失敗。
  • 蘋果為每個應用提供最多20GB的空間進行託管,這也是每個程式一次最多可以下載的資源數量。當然,應用程式一次最多隻能使用2GB的資源。
應用分片

注意20GB的限制是所有應用程式分片的總和,而是總共20GB。那什麼是應用程式分片呢?應用程式分片是iOS 9引入的,用於降低應用大小的一個特性。當應用被安裝後,它只會查詢與裝置對應的資源。例如,當資源被正確使用的時候,iPhone 6 Plus或6s Plus只會載入3x的圖示,而不會下載1x和2x的。所以整個按需載入資源的總的大小是20GB,包含我們上傳到App Store伺服器的所有裝置型別需要的資源總和。除此以外其它限制條件都是針對特定的裝置型別單獨設定的。

刪除按需載入資源

已經下載的按需載入資源只有當裝置空間不夠時才會被清理。當空間不夠時,按需載入資源系統會檢視裝置上的所有應用,並且根據資源的最後使用時間決定刪除哪個。如果應用程式正在執行,它擁有的資源永遠都不會被清理。

2. 分配和指定Tag

使用Xcode開啟啟動專案並且在模擬器中執行程式。它包含一組圖片,由三種顏色(紅、綠、藍)和四種形狀(圓、正方形、星形和六邊形)組合而成。當程式執行的時候,選擇Colors > Red,我們可以看到一個紅色的圓。

我們在程式中設定7個asset pack,每個包含一種顏色和一個形狀。按需請求資源的另外一個特性是每個資源可以設定多個Tag。比如紅色的圓可以同時是RedCircle包的一部分。

按需請求資源API對同一個資源不會下載兩次。換句話說,就是如果已經下載Red包,那麼在下載Circle包的時候,不會再次下載紅色圓形這個圖片。

在Xcode中,開啟Assets.xcassets。我們可以看到12張圖片。

下一步,選擇藍色方塊圖片集,並且開啟Attributes Inspector

Attributes Inspector中包含一個新的On Demand Resource Tags組,可以用來設定資源的標籤(Tag)。我們給藍色方塊設定BlueSquare標籤。

設定好資源的Tag後,開啟Xcode左側的Project Navigator,點選Resource Tags標籤並且選擇Prefetched進行過濾。

我們可以非常方便的看到每個資源包的大小以及裡面所包含的內容。Prefetched中顯示每個類別中的資源,並且允許在不同的類別中進行移動:

  • 初始安裝
  • 預取
  • 按需下載

這正是我之前所提到的三個類別。Prefetched Tag Order組中的資源在顯示的時候自動開始下載。

設定好了所有的圖片資源後,就可以開始訪問這些內容了。

3. 訪問按需請求資源

我們使用NSBundleResourceRequest獲取App Store伺服器託管的資源包。使用需要獲取的資源的Tag建立request物件。它會告訴系統我們所需要使用的資源包是哪個。而釋放這些物件則告訴系統我們不再需要這些資源包了。一定要注意不能超過2GB的資源限制。

在專案中,開啟DetailViewController.swift並且在DetailViewController中新增一下屬性。

var request: NSBundleResourceRequest!

下一步,替換viewDidAppear(_:)方法。

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    request = NSBundleResourceRequest(tags: [tagToLoad])
    request.beginAccessingResourcesWithCompletionHandler { (error: NSError?) -> Void in
        //  Called on background thread
        if error == nil {
            NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
                self.displayImages()
            })
        }
    }
}

剛才的程式碼中我們用Tag初始化了一個request物件。tagToLoad屬性是在前一個頁面設定的,表示我們要顯示的內容是什麼。

下一步,我們通過呼叫beginAccessingResourcesWithCompletionHandler(_:)開始下載特定Tag標明的資源包。這個方法自動訪問Tag對應的所有資源,並且開始下載。一旦獲取到資源後,其它程式碼一切照舊。

注意,如果我們只希望訪問已經下載的資源,而不想繼續加重內容。可以呼叫conditionallyBeginAccessingResourcesWithCompletionHandler(_:)

需要提醒的是,上面方法的執行完後,handler是在子執行緒中執行的,因此需要切換到主執行緒再更新UI。

我們已經成功的在程式裡使用了按需載入資源,簡單吧!

Xcode 7的有一個重要的除錯特性就是能夠檢視當前下載了那些資源包。點開Debug Navigator並且選擇Disk就可以看到了。

我們可以改變一下資源的下載優先順序,這樣有些內容就會總是立馬下載。同時還可以改變資源的儲存優先順序,比如HexagonStar會在CircleSquare之前清理。將程式碼改成一下樣子:

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    request = NSBundleResourceRequest(tags: [tagToLoad])

    request.loadingPriority = NSBundleResourceRequestLoadingPriorityUrgent
    NSBundle.mainBundle().setPreservationPriority(1.0, forTags: ["Circle", "Square"])
    NSBundle.mainBundle().setPreservationPriority(0.5, forTags: ["Hexagon", "Star"])

    request.beginAccessingResourcesWithCompletionHandler { (error: NSError?) -> Void in
        //  Called on background thread
        if error == nil {
            NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
                self.displayImages()
            })
        }
    }
}

初始化request物件後,我們可以設定loadingPriority屬性為NSBundleResourceRequestLoadingPriorityUrgent。當然,也可以設定0-1之間的任意值來表明載入優先順序。

這個常量自動設定請求為最高載入優先順序,但是這取決於當前的CPU活動情況。在某些情況下,如果CPU任務繁忙,資源的下載可能會被推遲。

下一步我們通過mainBundle的setPreservationPriority(_:forTags:)設定四個形狀的儲存優先順序。這時我們就可以確認,一旦按需載入系統需要清理一些資源的時候,就會先刪除HexagonStar

4. 最佳實踐

現在我們知道了如何在iOS應用裡使用按需載入資源。下面我想給大家介紹一些需要注意的地方。

讓每個Tag保持最小

為了減少資源的載入時間,使得每個資源更容易訪問,應該讓每個資源包保持儘可能的小。這樣就不會導致被過度清理。

例如,系統本身需要釋放50MB的空間,但是如果最合適被刪除的包是400MB,這樣就會導致350MB的內容被不必要的刪除。這就意味著下次需要的時候又要多下350MB。建議每個資源包的大小應該接近64MB。

提前下載資源

如果你的應用有一個非常容易預見的使用者操作過程,最好是提前就開始下載資源。這樣可以提高使用者體驗,而不需要他們等待太久。

遊戲是一個非常常見的場景。如果一個玩家已經完成了前5關,我們最好在他玩第6關的時候,開始下載第7關。

正確的停止對資源的訪問

如果不在需要訪問某個資源包,確保釋放NSBundleResourceRequest物件或者呼叫endAccessingResources方法。

這樣不僅可以防止我們的應用達到2GB的限制,而且可以幫助系統知道什麼時候需要訪問這些資源,從而更好的決定如何刪除資源來獲取更多空間。

5. tvOS上的按需訪問資源

在我的另外一篇關於tvOS的部落格提到tvOS的應用限制。比如最大程式大小為200MB,因此更應該使用按需方法資源。

tvOS和iOS的按需訪問資源的其它限制是一樣的,只是要注意tvOS僅使用1x的圖片,因此並不會因為應用程式分片的存在而減少空間。

總結

iOS 9和tvOS上的按需載入資源是用來減少程式大小和提供更好使用者體驗的一個非常重要的方法。它非常容易使用和設定,但是要注意它的載入時間和資料清理所帶來的一些問題。