你好,歡迎來到IOS教程網

On

編輯:IOS開發基礎

23.png

本文由唧唧歪歪翻譯自Apple文檔 On-Demand Resources Guide 
該文檔的上部分包含:按需加載資源基礎以及創建和編輯tag兩部分

(三)管理按需加載資源

下載和管理按需加載資源是由操作系統完成的。app使用NSBundleResourceRequest來:

  • 請求獲取按需加載資源。

  • 通知操作系統某些資源不再使用。

  • 更新下載的優先級。

  • 追蹤下載的進度。

  • 檢查存儲空間不足的通知。

當已下載的某些資源不再使用時,可以用NSBundle中的方法來設置保存優先級。

按需加載資源使用下面的4個方法來管理。

  1. app分配並初始化一個NSBundleResourceRequest對象。需要管理的tag必須在初始化時指定,不能更改。

  2. app請求獲取一個tag下的資源。如果這些資源需要下載,可以更新下載的優先級,追蹤下載進度。如果發生錯誤了,可以采取適當的措施。錯誤包括無效的tag、沒有網絡連接、無權使用蜂窩移動數據、沒有足夠的空間等等。

  3. app使用這些資源。這些資源的使用方式和該類型的其他資源一樣。

  4. app結束獲取這些資源,並通知系統不再使用這些資源。

在tag下載到設備後的任何時間都可以設置tag的保存優先級。

注意:每個NSBundleResourceRequest對象都只能用於一個請求訪問/結束訪問循環。

請求訪問

app必須在使用tag的資源之前先請求訪問這些tag。請求訪問的第一步是為tag創建一個NSBundleResourceRequest對象。一個tag可以由多個NSBundleResourceRequest對象來管理。

每個NSBundleResourceRequest實例管理同一個bundle下的加了tag的資源。使用下面的兩個方法來在初始化時設置被管理的tag和bundle:

  • 如果資源都在app的main bundle中,使用 initWithTags:。

  • 如果資源都在同一個自定義bundle中,使用 initWithTags:bundle: 。

注意:bundle可以設置為main bundle。

列表4-1展示了一個初始化資源管理器的一個例子,所有加tag的資源都在main bundle中。

列表4-1 初始化一個NSBundleResourceRequest實例

// Initialize an NSBundleResourceRequest with the desired tags
NSSet *tags = [NSSet setWithArray: @[@"birds", @"bridge", @"city"]];
// All the resources are in the main bundle so use the shorter initialization method
resourceRequest = [[NSBundleResourceRequest alloc] initWithTags:tags];

注意:tag和bundle只能在初始化時設置。

請求訪問資源

在初始化NSBundleResourceRequest實例之後,就是請求訪問了。當請求的所有tag下的所有資源都在本地存儲中時,操作系統會持有這些資源,並使用回調通知app這些資源已經可以使用了。更多信息參見第三步按需加載資源的生命周期。

有兩個方法來請求訪問。當資源已在設備上時,這兩個方法都可以允許訪問。不同的是當資源不在設備上時會做什麼。

  • beginAccessingResourcesWithCompletionHandler: 會從 App Store下載這些資源。

  • conditionallyBeginAccessingResourcesWithCompletionHandler: 不會下載資源。

兩個方法都會在回調block中返回結果。所有的資源都必須已經在設備上才能使用。列表4-2展示了方法beginAccessingResourcesWithCompletionHandler:。

列表4-2 使用beginAccessingResourcesWithCompletionHandler:

// Request access to the tags for this resource request
[resourceRequest beginAccessingResourcesWithCompletionHandler:
                                 ^(NSError * __nullable error)
    {
        // Check if there is an error
        if (error) {
            // There is a problem, update app state (should inform user if appropriate)
            self.resourcesLoaded = NO;
            return;
        }
 
        // the resources associated with the the tags are loaded, start using them
        self.resourcesAvailable = YES;
    }
];

注意:在允許訪問之後,不要使用同一個NSBundleResourceRequest實例再次請求訪問。

檢查tag是否已在設備上

有時當tag不在設備上時,你並不想開始下載它們。例如,當設備使用低帶寬網絡,並且高質量的圖片和聲音不在設備上時,你可以使用低質量資源。

當tag已在設備上,conditionallyBeginAccessingResourcesWithCompletionHandler:會允許訪問。如果tag不在設備上,app需要調用beginAccessingResourcesWithCompletionHandler:來下載它們。列表4-3展示了一個檢查tag是否在設備上的例子。

注意:如果conditionallyBeginAccessingResourcesWithCompletionHandler:返回YES,就不要調用beginAccessingResourcesWithCompletionHandler:了。

列表4-3 使用conditionallyBeginAccessingResourcesWithCompletionHandler:

// Request access to tags that may already be on the device
[resourceRequest conditionallyBeginAccessingResourcesWithCompletionHandler:
                                                 ^(BOOL resourcesAvailable)
    {
        // Check if the resources are available
        if (resourcesAvailable) {
            // the resources associated with the the tags are loaded, start using them
            self.highQualityResourcesAvailable = YES;
        } else {
            // The resources are not on the device and need to be loaded
            // Queue up a call to custom method for loading the tags using
            // beginAccessingResourcesWithCompletionHandler:
            dispatch_async(dispatch_get_main_queue(), ^{
                [self loadLowerQualityTags];
            }
        }
    }
];

何時請求tag

因為從App Store下載tag會花一些時間,你可以在需要使用tag之前請求tag。下載時間取決於總共要下載的大小,網絡連接的速度,以及操作系統能分配多少資源來處理下載。

在沒有帶寬限制的理想情況,在300Mbps的 802.11n或者LTE網絡(299.6Mbps)上,下載一個64MB的tag至少要用1.7秒。但實際情況是連接到因特網的速度要遠低於300Mbps。

下載優先級

資源請求有一個默認的優先級,這可以隨時更改,包括下載時。低優先級使用更少的操作系統資源,為其他任務騰出資源。這也會降低下載速度。低優先級有利於最大化app執行效率。列表4-4展示了一個更改請求優先級的例子。

列表4-4 更改下載優先級

// The priority is a between 0.0 and 1.0
self.resourceRequest.loadingPriority = 0.1;

提高下載優先級會使用更多的操作系統資源,相應地提高下載速度,降低app效率。如果下載很緊急,app可以將下載優先級設置為NSBundleResourceRequestLoadingPriorityUrgent。這會告訴操作系統盡可能多地分配資源來處理下載。一個使用場景就是用戶在下載完成之前什麼也做不了。列表4-5展示了一個當用戶需要等待時,設置緊急優先級的例子

列表4-5 提高請求的優先級

// Raise the priority based on the urgency
if (self.userWaiting) {
    // The user is waiting, request the maximum download time
    self.resourceRequest.loadingPriority = NSBundleResourceRequestLoadingPriorityUrgent;
} else {
    // Set a higher priority
    self.resourceRequest.loadingPriority = 0.8;
}

 上面的代碼直接使用了一個固定浮點數設置優先級。你也可以根據app的效率來更新下載優先級。

追蹤下載進度

在下載開始之後,請求會開始更新progress,這是一個NSProgress類型的屬性。app通過對progress.fractionCompleted進行KVO來追蹤下載進度。這需要開始和結束觀察,以及添加當值改變時執行的代碼。列表4-6展示了如何開始和結束觀察進度。列表4-7展示了當值改變時執行的代碼。

列表4-6 開始和結束追蹤下載進度

// Start observing fractionCompleted for the progress
[self.resourceRequest.progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:NULL];
 
// Stop observing fractionCompleted for the progress
[self.resourceRequest.progress removeObserver:self forKeyPath:@"fractionCompleted"];

列表4-7 當fractionCompleted的值改變時執行的代碼

//
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    // Check for the progress object and key
    if ((object == self.resourceRequest.progress) && ([keyPath isEqualToString:@"fractionCompleted])) {
        double progressSoFar = self.resourceRequest.progress.fractionCompleted;
        // do something with the value
    }
}

追蹤下載的兩個重要用途是:

  • 調整下載優先級。如果下載時間過長可以提高優先級,如果時間充裕可以降低優先級。

  • 為用戶提供下載進度反饋。可以使用一個簡單的進度條來反饋fractionCompleted的值。

暫停和取消下載

正在進行的下載可以被暫停、恢復、取消。著通過progress屬性,以及NSProgress提供的方法來完成。更多信息參見NSProgress類參考。

列表4-8 暫停、恢復、取消當前的下載

// Pause the current download
[self.resourceRequest.progress pause];
 
// Resume the current download
[self.resourceRequest.progress resume];
 
// Cancel the current download
[self.resourceRequest.progress cancel];

結束訪問

當app不再使用資源時,結束訪問能讓操作系統可以回收存儲空間。這也就是按需加載資源基礎中按需加載資源的生命周期的第4步。有2種方法結束訪問:

  • 給請求發送endAccessingResources,如列表4-9所示。

  • 釋放這個請求。

列表4-9 結束對tag的訪問

// End access by calling this method or deallocating the NSBUndleResourceRequest instance
[self.resourceRequest endAccessingResources];

在endAccessingResources調用之後,這個請求就不能再用於請求訪問了。如果app還需要訪問同一個tag,需要再重新創建一個NSBundleResourceRequest實例。

設置保留優先級

某些tag中的資源可能比其他的更重要。例如,應用內購買或者基本功能的資源就會被更頻繁地用到。app可以為這些tag設置一個高保留優先級。當操作系統開始清理tag時,會從最低保留優先級開始。

可以使用NSBundle的方法來設置和檢查保留優先級。

列表4-10 為tag檢查和設置保留優先級

// Check the preservation priority for the llama in-app purchase module
double currentPriority = [[NSBundle mainBundle] preservationPriorityForTag:@"iap-llamas"];
 
// Set the priority to the maximum of 1.0 (the default is 0.0)
// The call to set the priority takes a set of tags
NSSet *tags = [NSSet setWithArray: @[@"iap-llamas"]];
[[NSBundle mainBundle] setPreservationPriority:1.0 forTags:tags];

低存儲空間警告

當操作系統沒有辦法為當前正在請求的資源釋放出足夠的空間時,系統會發出一個通知。你的app應該停止訪問所有不再使用的tag,如上面結束訪問描述的。如果操作系統不能釋放出足夠空間,app會被終止。

例如,在一個有多個關卡的游戲中,用戶正在第4關,app請求第3、5、6關的tag。當低存儲空間警告發生時,app可以釋放第3、6關的tag。列表4-11展示了注冊低存儲空間通知的代碼。列表4-12展示了釋放不需要tag的方法。

列表4-11 注冊NSBundleResourceRequestLowDiskSpaceNotification通知

// End access by calling this method or deallocating the NSBUndleResourceRequest instance
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(lowDiskSpace:) name:NSBundleResourceRequestLowDiskSpaceNotification object:nil];

注冊通知一般由app delegate或者主view來完成。

列表4-12 低存儲空間通知的處理

// Notification handler for low disk space warning
-(void)lowDiskSpace:(NSNotification*)theNotification
{
    // Free the lower priority resource requests
    for (NSBundleResourceRequest *atRequest in self.lowPriorityRequests) {
        // End accessing the resources
        [atRequest endAccessingResources];
    }
 
    // clear lowPriorityRequests preventing multiple calls to endAccesingResource
    [self.lowPriorityRequests removeAllObjects];
}

lowPriorityRequests 不是由操作系統提供的。它是一個需要由app創建和維持的mutable set 。

(四)調試

在你開發的過程中可能會遇到幾類問題。不同類別的問題需要使用不同的工具來調試。主要有以下幾類問題:

  • 網絡連接:慢速或無網絡連接產生的問題。

  • 本地存儲空間:本地存儲空間不足無法下載。

  • 意外狀態:tag處於意外狀態。例如,一個tag顯示正在使用中,但app的所有模塊都已經結束訪問了。

意外狀態

調試意外狀態最有用的工具就是Xcode中的磁盤儀表了。磁盤儀表中會顯示tag的當前狀態,如下圖所示。

19.png

磁盤儀表會顯示每個tag的大小和狀態。大小是針對當前設備裁切後的。

tag的狀態

表5-1描述了磁盤儀表中tag可能處於的狀態。

表5-1 tag的狀態

77.png

使用磁盤儀表 

在模擬器或真機上運行app都可以使用磁盤儀表。

打開磁盤儀表。

  1. 選擇View > Navigators > Show Debug Navigator。

  2. 使用Scheme彈框選擇一個target和設備。

  3. 選擇Product > Run來啟動app。app會在選擇的設備上啟動,並連接調試器。

  4. 在Debug Navigator中的列表中點擊磁盤儀表。磁盤儀表會在workspace窗口的內容區顯示。

  5. 向下滾到直到顯示資源分類。你可以調整內容的大小來顯示全部tag。

附錄A:資源類型

下表列出了可以加tag的資源類型。

表A-1 資源類型

21.png

數據文件可以包括除了可執行的Swift、Objective-C、C、或者C++二進制包以外的任何類型數據。由腳本語言生成的文件可以用作資源。

附錄B:資源大小限制

內存大小

在App Store 提交時和app運行時,資源的使用的內存大小是有限制的。

表B-1 資源大小

22.png

  • Slicing。表示這個大小是在App裁切之前還是之後。

  • app二進制包。表示裁切後的下載到設備上的安裝包大小。

  • Initial install tags。裁切後標為初始安裝tag的全部大小。

  • Initial install and prefetched tags。裁切後標為初始安裝和預獲取tag的全部大小。

  • In use on-demand resources。裁切後app在任何時刻使用中的tag的大小。只要有一個NSBundleResourceRequest 對象訪問tag,tag就算是在使用中。

  • Hosted on-demand resources。由App Store托管的未裁切的大小。


該文檔的上部分包含:按需加載資源基礎以及創建和編輯tag兩部分

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved