按需加載資源開發(fā)指南(轉(zhuǎn))

來自:【http://benbeng.leanote.com/post/On-Demand-Resources-Guide 】本文翻譯自 Apple文檔 On-Demand Resources Guide(https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/On_Demand_Resources_Guide/Chapters/Introduction.html#//apple_ref/doc/uid/TP40015083-CH11-SW1

按需加載資源基礎(chǔ)
按需加載資源是由App Store托管的內(nèi)容,它和下載的app bundle是分開的。app請求一系列按需加載資源,而下載和存儲資源是由操作系統(tǒng)來管理。這些資源可以是除可執(zhí)行代碼外,bundle支持的任何類型。支持類型的完整列表,請參閱附錄a:按需加載的資源類型。

按需加載資源主要可以帶來以下的幾種好處:

初始資源的延遲加載。app有一些資源是主要功能要用到的,但在啟動時并不需要。將這些資源標(biāo)記為“初始需要”。操作系統(tǒng)在app啟動時會自動下載這些資源。例如,圖片編輯app有許多不常用的濾鏡。
app資源的延遲加載。app有一些只在特定情景下使用的資源,當(dāng)應(yīng)用可能要進(jìn)入這些場景時,會請求這些資源。例如,在一個有很多關(guān)卡的游戲中,用戶只需要當(dāng)前關(guān)卡和下一關(guān)卡的資源。
不常用資源的遠(yuǎn)程存儲。app有一些很少使用的資源,當(dāng)需要這些資源時會去請求它們。例如,當(dāng)app第一次打開時會展示一個教程,而這個教程之后就可能不會在用到。app在第一次啟動時請求教程的資源,這之后只在需要展示教程或者添加了新功能才去請求該資源。
應(yīng)用內(nèi)購買資源的遠(yuǎn)程存儲。app提供包含額外資源的應(yīng)用內(nèi)購買。app會在啟動完成后請求已購買模塊的資源。例如,用戶在一個鍵盤app內(nèi)購買了SuperGeeky表情包。應(yīng)用程序會在啟動完成后請求表情包的資源。
第一次啟動時必需資源的加載。app有一些資源只在第一次啟動時需要,之后的啟動不再需要。例如,app有一個只在第一次啟動時展示的教程。

在開發(fā)時,通過給按需加載資源分配一個或多個tag來區(qū)分。tag是一個你創(chuàng)建的字符串標(biāo)識符。你可以使用tag的名稱來區(qū)分這些資源在你的app中是如何使用的。例如,在一個游戲中,所有第五關(guān)的資源可以使用tag level-5。

在運(yùn)行時,你通過指定tag來請求訪問遠(yuǎn)程資源。操作系統(tǒng)會下載和這個tag關(guān)聯(lián)的所有資源,然后保留在存儲中,直到app不再使用它們。當(dāng)操作系統(tǒng)需要更多的存儲空間,它會清理一個或多個不再使用的tag關(guān)聯(lián)的資源。tag關(guān)聯(lián)的資源在被清理之前可能會在設(shè)備中保存一段時間。

繼續(xù)上面游戲的例子,游戲有許多關(guān)卡,用戶只需要當(dāng)前正在玩的關(guān)卡和可能玩的下一關(guān)卡的資源。下圖的上部展示了包括全部關(guān)卡資源的app bundle。可以通過為不同關(guān)卡和其他無需放在app內(nèi)的資源,創(chuàng)建不同tag的按需加載資源,來縮減app bundle的大小。下圖的底部顯示了一個更小的app,其中加了tag的資源由App Store托管。

還有一些其他的功能,比如讓你指定那些在app啟動之前必須加載的資源,為資源請求設(shè)置優(yōu)先級,追蹤資源下載的進(jìn)度,為不再使用的已下載tag設(shè)置保留優(yōu)先級。

按需加載資源的生命周期

當(dāng)用戶第一次啟動app時,設(shè)備上唯一的按需加載資源是那些設(shè)置為預(yù)獲取的。隨著用戶使用app,app會請求加了tag的資源,使用這些資源,然后告訴操作系統(tǒng)不再使用這些資源了。在這之后的某個時間,操作系統(tǒng)會清理一個或多個tag。

注意:app應(yīng)該請求tag,而不是單個資源。

當(dāng)你在開發(fā)按需加載資源時,可能會注意到請求下載某個tag時,其他tag也會被下載。這是因?yàn)椴僮飨到y(tǒng)處理的是資源包(asset pack),這些資源包會為共享資源的下載進(jìn)行優(yōu)化。一個tag可能對應(yīng)多個資源包。資源包是在構(gòu)建app時由Xcode生成的。

加了tag的資源的生命周期如下所示:

1、加了tag的資源由App Store托管。

   這發(fā)生在app第一次啟動和之前加載的tag被清理時。

2、加tag的資源被下載到設(shè)備上

  這發(fā)生在app請求一個設(shè)備上沒有的tag之后,或者發(fā)生在操作系統(tǒng)下載初始資源。



  下圖中展示的是,app請求Level11和Foresttag關(guān)聯(lián)的資源,然后下載到設(shè)備上。

3、操作系統(tǒng)將tag保存在本地存儲中,并通知app這些tag已經(jīng)可以使用。

  這發(fā)生在一個tag下的所有資源都已成功下載之后,或者tag對應(yīng)的資源都已緩存在本地存儲中。





   在tag可用之后,app就可以使用tag關(guān)聯(lián)的資源了。按需加載資源的使用方式和使用下載的app中資源包一樣。下圖展示了下載的資源就像是app的虛擬成員一樣。

4、操作系統(tǒng)釋放本地存儲中的tag。

   被釋放的tag還會保存在設(shè)備中,直到它再次被請求,或者從本地存儲中被清理掉。當(dāng)一個tag沒有被任何請求持有時,tag就可以被清理。在tag被清理之前,它還可能在設(shè)備上保存一段時間,包括app多次啟動。請求一個tag會持有該tag,然后返回上面的步驟3。



    清理tag會返回到上面的步驟1。你可以為tag設(shè)置保存優(yōu)先級來影響清理的順序。更多信息參見管理按需加載資源。

啟用按需加載資源
對于支持iOS 9.0及以后的app,按需加載資源是默認(rèn)開啟的。你也可以在target的build settings中手動更改。

啟用或關(guān)閉按需加載資源:

1、在project navigator中選擇工程文件。

2、在project editor中選擇對應(yīng)的target。

3、選擇Build Settings選項(xiàng)卡。

4、展開Assets分類。

提示:可以在Build Settings選項(xiàng)卡右上角的搜索框中輸入“Assets”,來快速定位到Assets分類。

5、設(shè)置Enable On-Demand Resources的值。

Yes為這個target開啟按需加載資源。
No為這個target關(guān)閉按需加載資源。

創(chuàng)建和編輯tag
tag用來區(qū)分和管理按需加載資源。給一個資源添加一個或多個tag就能將其標(biāo)記為按需加載資源。在運(yùn)行時,所有管理按需加載資源的調(diào)用都是和tag相關(guān),而不是單個資源。在運(yùn)行時管理按需加載資源在管理按需加載資源中有講到。

Xcode中的資源 tag

Xcode提供了管理tag和相關(guān)資源的工具。包括創(chuàng)建和編輯tag,添加和刪除tag下的資源,指定操作系統(tǒng)在什么時刻下載tag關(guān)聯(lián)的資源。

Resource Tags選項(xiàng)卡

Resource Tags選項(xiàng)卡在project editor中,用來瀏覽和編輯工程中全部的tag和相關(guān)資源。選項(xiàng)卡包括:

添加按鈕:用來創(chuàng)建新的tag。
搜索框:用來查找指定的tag或資源。
tag編輯器:用來更改tag的名稱,刪除tag,增刪tag下的資源。tag下全部資源所占的存儲大小會顯示在tag名稱右邊的圓括號中。大小是依據(jù)最后一次構(gòu)建所選擇的運(yùn)行設(shè)備來計(jì)算的。
tag視圖選擇器:用來在查看全部tag和查看預(yù)獲取分類tag之間切換。tag可以在app安裝時下載,在安裝之后預(yù)獲取,或者在app運(yùn)行時按需加載。預(yù)取優(yōu)先級編輯器也會顯示每個分類中資源所占的存儲大小。大小是依據(jù)最后一次構(gòu)建所選擇的運(yùn)行設(shè)備來計(jì)算的。

按需加載資源tag框

在按需加載資源tag框中,你可以快速瀏覽一個文件夾或資源,并為其添加刪除tag。在Asset Catalog中,如果文件夾或者某個類型的資源可以加tag的話,這個框顯示在Attributes inspector中,如下圖所示。

創(chuàng)建tag

創(chuàng)建tag的第一步是檢查你的app在運(yùn)行時如何使用資源。看看你的app是否有以下幾種資源:

在任何時刻都必須是app的一部分。
可以提升用戶體驗(yàn),但在啟動app時不是必需的。包括高分辨率素材、高質(zhì)量聲音等等。
在特定場景下才需要。

上面說的后兩類的就有可能是按需加載資源。在app第一次啟動后馬上就要用到的資源可以設(shè)置為在安裝后自動預(yù)獲取。操作方法見預(yù)獲取tag。

每個tag下的資源總計(jì)大小不能超過2GB。按需加載資源的總計(jì)大小不能超過20GB。按需加載資源的大小是不算在app bundle的大小中的。更多信息,參見附錄B:按需加載資源的大小限制。

一個tag的理想大小是不超過64MB。這能很好地平衡下載速度和本地存儲大小,當(dāng)設(shè)備的存儲空間不足時可以進(jìn)行清理。

tag的名稱最好能區(qū)分出資源是如何使用的。例如,所有和森林場景有關(guān)的資源就可以起名叫forest-scenery。

創(chuàng)建好tag后就可以分配給資源了。一個資源可以添加多個tag。例如在下圖中,游戲的資源是按關(guān)卡和地點(diǎn)來分割的。

如何創(chuàng)建一個新的tag

1、在project navigator中, 選擇工程文件。

2、為需要新tag的target打開project editor 。

3、選擇 Resource Tags 選項(xiàng)卡。

4、點(diǎn)擊選項(xiàng)卡左上角的添加按鈕(+)。

    如下圖所示,會顯示一個新的tag輸入框。

5、輸入tag的名稱來替換占位符。

為資源添加tag

可以為任何有效的按需加載資源類型添加tag。具體的有效類型,參見附錄A:

在Asset Catalog中添加tag

1、在project navigator中, 選擇Asset Catalog。

2、在列表中選擇一項(xiàng) 。

3、為選擇的項(xiàng)目打開Attributes inspector。

4、在按需加載資源tag框,輸入tag的名稱。

    Xcode會根據(jù)輸入的字符進(jìn)行自動補(bǔ)全提示。

5、按下Return鍵來確認(rèn)輸入的tag名稱。

注意:輸入一個新的名稱,并按下Return鍵,會創(chuàng)建一個新的tag。

在按需加載資源tag框?yàn)楣こ讨械哪硞€文件添加tag

1、在project navigator中, 選擇一個文件。

2、打開實(shí)用工具區(qū),點(diǎn)擊File inspector 按鈕。

3、在按需加載資源tag框,輸入tag的名稱。

    Xcode會根據(jù)輸入的字符進(jìn)行自動補(bǔ)全提示。

4、按下Return鍵來確認(rèn)輸入的tag名稱。

注意:輸入一個新的名稱,并按下Return鍵,會創(chuàng)建一個新的tag。

使用Resource Tags選項(xiàng)卡來給工程中的文件添加tag

1、在project navigator中, 選擇工程文件。

2、為target打開project editor 。

3、選擇 Resource Tags 選項(xiàng)卡。

4、在搜索框中,輸入tag的名稱。列表會根據(jù)搜索的文字進(jìn)行過濾。

5、點(diǎn)擊添加按鈕(+),會彈出一個對話框。

6、在輸入框中,輸入資源文件的名稱。

7、選中想要的資源文件,點(diǎn)擊Add。這個資源文件就加上tag了。

注意: Asset Catalogs中的資源必須使用按需加載資源tag框來添加tag。

從tag中刪除資源

可以通過在按需加載資源tag框刪除tag,來將資源從tag中刪除。也可以在Resource Tags 選項(xiàng)卡中刪除資源。

使用Resource Tags選項(xiàng)卡來刪除tag下的資源

在project navigator中, 選擇工程文件。
為target打開project editor 。
選擇 Resource Tags 選項(xiàng)卡。
在搜索框中,輸入tag的名稱。列表會根據(jù)搜索的文字進(jìn)行過濾。
在tag對應(yīng)的行中,點(diǎn)擊左側(cè)的小箭頭來展開tag編輯器(如果沒有展開的話)。
在tag編輯器中,選擇列表中的資源文件。
在tag編輯器中,點(diǎn)擊刪除按鈕(-)。

在Asset Catalog中添加tag

在project navigator中, 選擇Asset Catalog。
在列表中選擇一項(xiàng) 。
為選擇的項(xiàng)目打開Attributes inspector。
在按需加載資源tag框,選擇一個tag,按下Delete鍵。

在按需加載資源tag框?yàn)楣こ讨械哪硞€文件刪除tag

在project navigator中, 選擇一個文件。
打開實(shí)用工具區(qū),點(diǎn)擊File inspector 按鈕。
在按需加載資源tag框,選擇一個tag,按下Delete鍵。

預(yù)獲取的tag

通常來說,操作系統(tǒng)是不會下載一個tag下的資源,直到app請求一個tag并且該tag的資源沒有在設(shè)備上緩存。一些tag中會包含啟動時需要或者啟動后馬上要用到的資源。

在Resource Tags選項(xiàng)卡的Prefetched界面下,可以把tag分配給三個預(yù)獲取優(yōu)先級分類的其中一個。界面展示了按預(yù)獲取分類分組的tag。tag可以在分類間拖動。

初始安裝tag(Initial install tags)。只有在初始安裝tag下載到設(shè)備后,app才能啟動。這些資源會在下載app時一起下載。這部分資源的大小會包括在App Store中app的安裝包大小。如果這些資源從來沒有被NSBundleResourceRequest對象獲取過,就有可能被清理掉。
按順序預(yù)獲取tag(Prefetch tag order)。在app安裝后會開始下載tag。tag會按照此處指定的順序來下載。
按需下載(Dowloaded only on demand)。當(dāng)app請求一個tag,且tag沒有緩存時,才會下載該tag。

將tag設(shè)置為隨app安裝

在project navigator中, 選擇工程文件。
為target打開project editor 。
選擇 Resource Tags 選項(xiàng)卡。
在tag視圖選擇器,點(diǎn)擊Prefetched。
在內(nèi)容區(qū),點(diǎn)擊Initial Install Tags分類旁邊的小箭頭。
在內(nèi)容區(qū),找到你想要隨app安裝的tag。
點(diǎn)擊并拖動該tag到Initial Install Tags列表。

將tag設(shè)置為預(yù)獲取

在project navigator中, 選擇工程文件。
為target打開project editor 。
選擇 Resource Tags 選項(xiàng)卡。
在tag視圖選擇器,點(diǎn)擊Prefetched。
在內(nèi)容區(qū),點(diǎn)擊Prefetched Tag Order分類旁邊的小箭頭。
在內(nèi)容區(qū),找到你想要預(yù)獲取的tag。
點(diǎn)擊并拖動該tag到Prefetched Tag Order列表。 tag在列表的位置會決定下載的順序。列表最上面的tag會最先下載。

將tag設(shè)置為只在app請求時下載

在project navigator中, 選擇工程文件。
為target打開project editor 。
選擇 Resource Tags 選項(xiàng)卡。
在tag視圖選擇器,點(diǎn)擊Prefetched。
在內(nèi)容區(qū),點(diǎn)擊Downloaded Only On Demand分類旁邊的小箭頭。
在內(nèi)容區(qū),找到你想要按需下載的tag。
點(diǎn)擊并拖動該tag到Downloaded Only On Demand列表。

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

請求獲取按需加載資源。
通知操作系統(tǒng)某些資源不再使用。
更新下載的優(yōu)先級。
追蹤下載的進(jìn)度。
檢查存儲空間不足的通知。

當(dāng)已下載的某些資源不再使用時,可以用NSBundle中的方法來設(shè)置保存優(yōu)先級。

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

app分配并初始化一個NSBundleResourceRequest對象。需要管理的tag必須在初始化時指定,不能更改。
app請求獲取一個tag下的資源。如果這些資源需要下載,可以更新下載的優(yōu)先級,追蹤下載進(jìn)度。如果發(fā)生錯誤了,可以采取適當(dāng)?shù)拇胧ee誤包括無效的tag、沒有網(wǎng)絡(luò)連接、無權(quán)使用蜂窩移動數(shù)據(jù)、沒有足夠的空間等等。
app使用這些資源。這些資源的使用方式和該類型的其他資源一樣。
app結(jié)束獲取這些資源,并通知系統(tǒng)不再使用這些資源。

在tag下載到設(shè)備后的任何時間都可以設(shè)置tag的保存優(yōu)先級。

注意:每個NSBundleResourceRequest對象都只能用于一個請求訪問/結(jié)束訪問循環(huán)。

請求訪問

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

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

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

注意:bundle可以設(shè)置為main bundle。

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

列表4-1 初始化一個NSBundleResourceRequest實(shí)例

// 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只能在初始化時設(shè)置。

請求訪問資源

在初始化NSBundleResourceRequest實(shí)例之后,就是請求訪問了。當(dāng)請求的所有tag下的所有資源都在本地存儲中時,操作系統(tǒng)會持有這些資源,并使用回調(diào)通知app這些資源已經(jīng)可以使用了。更多信息參見第三步按需加載資源的生命周期。

有兩個方法來請求訪問。當(dāng)資源已在設(shè)備上時,這兩個方法都可以允許訪問。不同的是當(dāng)資源不在設(shè)備上時會做什么。

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

兩個方法都會在回調(diào)block中返回結(jié)果。所有的資源都必須已經(jīng)在設(shè)備上才能使用。列表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實(shí)例再次請求訪問。

檢查tag是否已在設(shè)備上

有時當(dāng)tag不在設(shè)備上時,你并不想開始下載它們。例如,當(dāng)設(shè)備使用低帶寬網(wǎng)絡(luò),并且高質(zhì)量的圖片和聲音不在設(shè)備上時,你可以使用低質(zhì)量資源。

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

注意:如果conditionallyBeginAccessingResourcesWithCompletionHandler:返回YES,就不要調(diào)用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

因?yàn)閺腁pp Store下載tag會花一些時間,你可以在需要使用tag之前請求tag。下載時間取決于總共要下載的大小,網(wǎng)絡(luò)連接的速度,以及操作系統(tǒng)能分配多少資源來處理下載。

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

下載優(yōu)先級

資源請求有一個默認(rèn)的優(yōu)先級,這可以隨時更改,包括下載時。低優(yōu)先級使用更少的操作系統(tǒng)資源,為其他任務(wù)騰出資源。這也會降低下載速度。低優(yōu)先級有利于最大化app執(zhí)行效率。列表4-4展示了一個更改請求優(yōu)先級的例子。

列表4-4 更改下載優(yōu)先級

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

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

列表4-5 提高請求的優(yōu)先級

// 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;
}

上面的代碼直接使用了一個固定浮點(diǎn)數(shù)設(shè)置優(yōu)先級。你也可以根據(jù)app的效率來更新下載優(yōu)先級。

追蹤下載進(jìn)度

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

列表4-6 開始和結(jié)束追蹤下載進(jìn)度

// 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 當(dāng)fractionCompleted的值改變時執(zhí)行的代碼

//
-(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
}
}

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

調(diào)整下載優(yōu)先級。如果下載時間過長可以提高優(yōu)先級,如果時間充裕可以降低優(yōu)先級。
為用戶提供下載進(jìn)度反饋。可以使用一個簡單的進(jìn)度條來反饋fractionCompleted的值。

暫停和取消下載

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

列表4-8 暫停、恢復(fù)、取消當(dāng)前的下載

// Pause the current download
[self.resourceRequest.progress pause];

// Resume the current download
[self.resourceRequest.progress resume];

// Cancel the current download
[self.resourceRequest.progress cancel];

結(jié)束訪問

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

給請求發(fā)送endAccessingResources,如列表4-9所示。
釋放這個請求。

列表4-9 結(jié)束對tag的訪問

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

在endAccessingResources調(diào)用之后,這個請求就不能再用于請求訪問了。如果app還需要訪問同一個tag,需要再重新創(chuàng)建一個NSBundleResourceRequest實(shí)例。

設(shè)置保留優(yōu)先級

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

可以使用NSBundle的方法來設(shè)置和檢查保留優(yōu)先級。

列表4-10 為tag檢查和設(shè)置保留優(yōu)先級

// 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];

低存儲空間警告

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

例如,在一個有多個關(guān)卡的游戲中,用戶正在第4關(guān),app請求第3、5、6關(guān)的tag。當(dāng)?shù)痛鎯臻g警告發(fā)生時,app可以釋放第3、6關(guān)的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 不是由操作系統(tǒng)提供的。它是一個需要由app創(chuàng)建和維持的mutable set 。

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

網(wǎng)絡(luò)連接:慢速或無網(wǎng)絡(luò)連接產(chǎn)生的問題。
本地存儲空間。本地存儲空間不足無法下載。
意外狀態(tài)。tag處于意外狀態(tài)。例如,一個tag顯示正在使用中,但app的所有模塊都已經(jīng)結(jié)束訪問了。

意外狀態(tài)

調(diào)試意外狀態(tài)最有用的工具就是Xcode中的磁盤儀表了。磁盤儀表中會顯示tag的當(dāng)前狀態(tài),如下圖所示。

磁盤儀表會顯示每個tag的大小和狀態(tài)。大小是針對當(dāng)前設(shè)備裁切后的。

tag的狀態(tài)

表5-1描述了磁盤儀表中tag可能處于的狀態(tài)。

表5-1 tag的狀態(tài)

狀態(tài) 描述
未下載 在此調(diào)試會話中,tag還沒有被下載到設(shè)備上。
部分已下載 可能由于連接中斷造成tag的一部分已下載。
下載中 tag正在下載中。可能會顯示一個進(jìn)度條。
已下載 tag已在設(shè)備上,且沒有被任何NSBundleResourceRequest 對象使用。
使用中 tag已在設(shè)備上,且正在被app使用。
被清理 tag之前在設(shè)備上,現(xiàn)在已經(jīng)被清理出本地存儲。在被使用前,必須先下載。

使用磁盤儀表

在模擬器或真機(jī)上運(yùn)行app都可以使用磁盤儀表。

打開磁盤儀表。

選擇View > Navigators > Show Debug Navigator。
使用Scheme彈框選擇一個target和設(shè)備。
選擇Product > Run來啟動app。app會在選擇的設(shè)備上啟動,并連接調(diào)試器。
在Debug Navigator中的列表中點(diǎn)擊磁盤儀表。磁盤儀表會在workspace窗口的內(nèi)容區(qū)顯示。
向下滾到直到顯示資源分類。你可以調(diào)整內(nèi)容區(qū)的大小來顯示全部tag。

附錄A:資源類型
下表列出了可以加tag的資源類型。

表A-1 按需加載資源類型

類型 Asset catalog 文件
圖像文件 - √
多媒體文件 - √
Asset catalog image set √ -
Asset catalog folder √ -
SpriteKit scene - √
SpriteKit texture atlas √ √
SpriteKit particle - √
WatchKit Complication √ -
OpenGL shader - √
數(shù)據(jù)文件 √ √

數(shù)據(jù)文件可以包括除了可執(zhí)行的Swift、Objective-C、C、或者C++二進(jìn)制包以外的任何類型數(shù)據(jù)。由腳本語言生成的文件可以用作按需加載資源。

附錄B:資源大小限制
存儲空間大小

在App Store 提交時和app運(yùn)行時,按需加載資源使用的存儲空間大小是有限制的。

表B-1 資源大小

項(xiàng)目 大小 Slicing
app二進(jìn)制包 2GB √
Initial install tags 2GB √
Initial install and prefetched tags 4GB √
In use on-demand resources 2GB √
Hosted on-demand resources 20GB -

Slicing。表示這個大小是在App裁切之前還是之后。
app二進(jìn)制包。表示裁切后的下載到設(shè)備上的安裝包大小。
Initial install tags。裁切后標(biāo)為初始安裝tag的全部大小。
Initial install and prefetched tags。裁切后標(biāo)為初始安裝和預(yù)獲取tag的全部大小。
In use on-demand resources。裁切后app在任何時刻使用中的tag的大小。只要有一個NSBundleResourceRequest 對象訪問tag,tag就算是在使用中。
Hosted on-demand resources。由App Store托管的tag未裁切的大小。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容