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上的按需加載資源是用來減少程序大小和提供更好用戶體驗的一個非常重要的方法。它非常容易使用和設置,但是要注意它的加載時間和數據清理所帶來的一些問題。

戴維營學院(高級開發視頻): http://v.diveinedu.com

潛心俱樂部(iOS面試必備): http://divein.club


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章