本文由唧唧歪歪翻译自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实例

1
2
3
4
// 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 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 更改下载优先级

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

提高下载优先级会使用更多的操作系统资源,相应地提高下载速度,降低app效率。如果下载很紧急,app可以将下载优先级设置为NSBundleResourceRequestLoadingPriorityUrgent。这会告诉操作系统尽可能多地分配资源来处理下载。一个使用场景就是用户在下载完成之前什么也做不了。列表4-5展示了一个当用户需要等待时,设置紧急优先级的例子

列表4-5 提高请求的优先级

1
2
3
4
5
6
7
8
// 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 开始和结束追踪下载进度

1
2
3
4
5
// 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的值改变时执行的代码

1
2
3
4
5
6
7
8
9
//
-(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 暂停、恢复、取消当前的下载

1
2
3
4
5
6
7
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的访问

1
2
// 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检查和设置保留优先级

1
2
3
4
5
6
7
// 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通知

1
2
// 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 低存储空间通知的处理

1
2
3
4
5
6
7
8
9
10
11
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的当前状态,如下图所示。

磁盘仪表会显示每个tag的大小和状态。大小是针对当前设备裁切后的。

tag的状态

表5-1描述了磁盘仪表中tag可能处于的状态。

表5-1 tag的状态

使用磁盘仪表 

在模拟器或真机上运行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 资源类型

数据文件可以包括除了可执行的Swift、Objective-C、C、或者C++二进制包以外的任何类型数据。由脚本语言生成的文件可以用作资源。

附录B:资源大小限制

内存大小

在App Store 提交时和app运行时,资源的使用的内存大小是有限制的。

表B-1 资源大小

  • 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两部分

转载于:https://my.oschina.net/hejunbinlan/blog/474868

On-Demand Resources Guide中文版(按需加载资源--下)相关推荐

  1. On-Demand Resources Guide中文版(按需加载资源--上)

    本文翻译由唧唧歪歪翻译自Apple文档 On-Demand Resources Guide On-Demand Resources Guide中文版(按需加载资源--下)包含管理按需加载资源.调试以及 ...

  2. iOS和tvOS游戏按需加载资源简介

    2019独角兽企业重金招聘Python工程师标准>>> 戴维营教育翻译:感谢Davis Allie的An Introduction to On-Demand Resources on ...

  3. Webpack实战(九):实现资源按需加载-资源异步加载

    第八篇[<教你搞懂webpack如果实现代码分片(code splitting)>] (https://blog.csdn.net/lfcss/article/details/104099 ...

  4. element-ui 按需加载情况下覆盖预置scss变量

    element-ui的默认主题theme-chalk是通过scss预编译的,可以通过修改scss变量达到修改主题的目的. 在项目中改变 SCSS 变量 Element 的 theme-chalk 使用 ...

  5. ensure函数_webpack中利用require.ensure()实现按需加载

    webpack中的require.ensure()可以实现按需加载资源包括js,css等,它会给里面require的文件单独打包,不和主文件打包在一起,webpack会自动配置名字,如0.js,1.j ...

  6. 按需加载图片、html代码、js代码,前端页面性能优化

    加载一个比较长的页面,或者图片比较多的页面,如果把资源一次性全部加载,在网络不是很好的情况下可能造成页面留白现象,用户体验极差! 因此呢,我们可以把用户暂时不会看到或用到的资源先不加载,在某个条件下, ...

  7. ensure函数_webpack中require.ensure()实现按需加载

    webpack中的require.ensure()可以实现按需加载资源包括js,css等,它会给里面require的文件单独打包,不和主文件打包在一起,webpack会自动配置名字,如0.js,1.j ...

  8. yepnope.js 异步加载资源文件

    yepnope.js是一个能够根据输入条件来选择性异步加载资源文件的js脚本,可以在页面上仅加载用户需要的js/css. yepnope的优点: 可以同时处理javascript以及css 能够按条件 ...

  9. vue按需加载组件_微人事首页加载速度提高了 5 倍,我都做了什么?

    「本文之前发过,但是比较零散,这里我把用到的方案都汇总一下,方便大家索引,有需要的小伙伴可以收藏下方便查找.里边提到的几种方案,大家都可以对照着视频试一下」 ElementUI 按需加载: 服务端开启 ...

最新文章

  1. 如何解决MySQL order by limit语句的分页数据重复问题?
  2. 掌握这几种 Markdown 语法你就够了
  3. 数据结构与算法之插入排序
  4. Java中装箱与拆箱
  5. 在VMware开启此虚拟机时出现内部错误
  6. 如何看屈曲因子_Abaqus 非线性屈曲分析方法
  7. flask-sqlalchemy Multiple Databases
  8. spss多元线性回归散点图_案例分析 | 多元线性回归及SPSS操作
  9. windows系统bat批处理 执行后 隐藏批处理本身
  10. jabc spring
  11. 为什么我推荐你立刻使用Java 8 Stream?性能逆天了
  12. vs2008安装包及安装教程
  13. DotNetCore CAP
  14. Word文档标题自动增加序号
  15. 基于matlab的微分例题,基于MATLAB的rlc电路模型仿真例题.doc
  16. python数据采集培训
  17. 【湖上日出】从零开始通过拖拉拽可视化的方式制作动态风景,免手写CSS
  18. 深圳宝安周边公司出行团建户外一日游
  19. stm32h750/stm32h743原理图和pcb源文件
  20. 用友U8cloud智能财务精细管控

热门文章

  1. 第二讲:项目运行环境 事业环境因素 (EEF) 和组织过程资产 (OPA)
  2. 高精度电流源如何设计出来
  3. 部署skyWalking
  4. 如何利用开关量信号传输装置实现工厂智能化技改?
  5. mt6762/mt6765平台i2c驱动能力修改与波形优化
  6. 学习淘淘商城第十六课(展示后台管理页面)
  7. 常见License错误代码
  8. 计算天数(C语言)——罡罡同学
  9. 【AP5904】三功能 2.5-5V 1.8A LED车灯 手电筒驱动芯片
  10. java基础编程题及答案,三面蚂蚁金服(交叉面)定级阿里P6