> For the complete documentation index, see [llms.txt](https://ryukiedev.gitbook.io/wiki/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ryukiedev.gitbook.io/wiki/ios/you-hua/01.apple-guan-fang-zi-yuan-shou-shen-fang-an-odr-yi-chu-jian.md).

# 01.Apple官方资源瘦身方案ODR（一）：初见

## Hi 👋

* Wechat: RyukieW
* 📦 [技术文章归档](https://ryukiedev.gitbook.io/wiki/)
* 🐙 [Github](https://github.com/RyukieSama)

|                                   我的个人项目                                   |                     扫雷Elic 无尽天梯                    |                         梦见账本                        |
| :------------------------------------------------------------------------: | :------------------------------------------------: | :-------------------------------------------------: |
|                                     类型                                     |                         游戏                         |                          财务                         |
| [AppStore](https://apps.apple.com/cn/developer/rongqing-wang/id1264542103) | [Elic](https://apps.apple.com/cn/app/id1488204246) | [Umemi](https://apps.apple.com/cn/app/id1498426607) |

## 前言

应用瘦身是个老生常谈的话题。苹果其实对于应用瘦身也有着自己的一些说明。本文就将结合官方文档进行阐述。

## 一、 什么是应用瘦身？

在 `iOS`, `tvOS`, `watchOS` 上 `AppStore` 和操作系统根据用户设备和操作系统的性能对App进行裁剪优化。这个优化的过程就是 `应用瘦身`。

* 你可以用最少的空间创建功能齐全持续更新的应用
* 更快的下载速度
* 更多的空间安装其他App
* 带来更好的用户体验

## 二、 剪切 Slicing (iOS, tvOS)

`Slicing` 是针对不同设备不同系统创建部署变体应用包的过程。每个变体只包含当前设备和操作系统所需要的资源和可执行文件。你只需要打包上传完整版本的应用到 `AppConnect`。`AppStore` 将会根据不同的设备及操作系统版本创建分发不同的变体。

用 `asset` 储存图片资源以便 `AppStore` 为不同的变体选择资源加载。当用户安装应用时，将会下载安装一个针对其设备系统版本的变体。

`Xcode` 将会在本地 `build` 和 `run` 的时候模拟进行 `Slicing`。当你 `archive` 的时候 `Xcode` 将会包含所有构建版本并允许你导出变体包。

***下图很好的演示了这个过程***

![Xcode](/files/-Mg45Rzdi0YOaYnwXTxm)

### 思考

这里提到 `asset` 会根据架构不同进行资源加载，所以我们平时不要把资源直接放到 `Bundle` 中，而跳过了这个优化的过程。

## 三、 Bitcode

`Bitcode` 是编译器的一层间接。你上传到 `AppConnect` 的打开了`Bitcode`的 `App` 将会在 `AppStore` 被 `编译` 和 `链接`。包含了 `Bitcode` 将会使`Apple` 可以在未来再优化你的应用包而不用再重新上传。

对于 `iOS` 应用 `Bitcode` 是默认非必须选的。`watchOS` 和 `tvOS` 是必选的。如果想要支持 `Bitcode` 那么你所有用到的库都要支持 `Bitcode`。

### 符号表

`Xcode` 默认会隐藏你的应用的符号表，所以对于 `Apple` 它是不可见的。当你上传你的 `App` 到 `AppConnect` 的时候你`可以选择包含符号表``。包含符号表会使Apple` 为你提供崩溃日志。

如果你想要自己收集崩溃日志，那么你可以不上传符号表。你可以在发布应用后下载 `Bitcode` 编译后的符号表。

## 四、 ODR: On-Demand Resources (iOS, tvOS)

这些资源主要是图片和声音。`AppStore` 将其保存管理在苹果的服务器。`AppStore` 将会按需使用资源优化变体包。后面将以 `ODR` 进行简称。

`ODR` 提供了更好的用户体验：

* 应用体积更小，下载更快，提升初次启动速度
* 资源会在后台下载
* 操作系统将会在磁盘资源不够的时候清理 `ODR`

![on\_demand\_resources](/files/-Mg45RzhIXxmSXBgEPTJ)

* 想要支持`ODR`，可以阅读文档：
  * [On-Demand Resources Guide](https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/On_Demand_Resources_Guide/index.html#//apple_ref/doc/uid/TP40015083)
  * [NSBundleResourceRequest](https://developer.apple.com/documentation/foundation/nsbundleresourcerequest)

### 4.1 使用须知

`App` 负责请求这些资源，操作系统负责 `下载` 和 `存储`。应用使用资源，然后请求。下载后，资源可能会通过多个启动周期保留在设备上，使访问速度更快。

资源可以是 `Bundle` 支持的任何类型的资源，但 `可执行程序` 除外。`Table 1-1` 显示支持按需资源类型，并指示这些类型是否以 `文件` 或 `Asset` 包含在项目中。

***Table 1-1:*** On-demand resource types

|           Type          | Asset catalog | File |
| :---------------------: | :-----------: | :--: |
|        Data file        |       ✓       |   ✓  |
|          Image          |       ✓       |   ✓  |
|      OpenGL shader      |       x       |   ✓  |
|    SpriteKit particle   |       x       |   ✓  |
|     SpriteKit scene     |       x       |   ✓  |
| SpriteKit texture atlas |       ✓       |   ✓  |
|   Apple TV Image Stack  |       ✓       |   ✓  |

> 数据文件可以包含任何类型的数据，但可执行的 `Swift`、 `Objective-C`、 `C` 或 `C++` 代码除外。`脚本语言生成的文件` 可以是按需加载的。

### 4.2 ODR 的好处

* 较小的应用程序大小。
  * 用户下载的应用捆绑包的大小较小，因此下载速度更快，设备上的存储空间更大。
* 应用资源懒加载。
  * 该应用程序的资源仅在某些状态下使用。当应用可能进入适当的状态时，需要资源。
  * 例如，在具有多个级别的游戏中，用户只需要与当前和下一个级别相关的资源。
* 远程存储很少使用的资源。
  * 该资源很少使用。根据需要要求提供资源。
  * 例如，应用教程通常在第一次打开应用后显示一次，可能永远不会再次使用。该应用程序要求在第一次启动时请求教程，然后仅在需要或添加新功能时再请求教程。
* 应用内购买资源的远程存储。
  * 如果应用程序提供应用内购买，包括其他资源。应用启动后会请求购买模块的资源。
  * 例如，用户在键盘应用中购买表情包。应用程序在启动完成后请求该包。

## 五、 ODR 标签的工作原理

通过分配 `一个或多个标签` 来识别开发过程中的按需加载资源。`标签` 是您创建的字符串标识符。您可以使用 `标签的名称` 来识别应用中如何使用包含的资源。 ***例如*** 在游戏中，使用标记 `level-5` 来处理与 `level-5` 相关的每个资源。

* 在运行时
  * 通过指定一组标签请求访问远程资源。操作系统下载标记为这些标签的资源，然后存储，直到应用使用完为止。
* 当操作系统需要更多存储时
  * 它会清除不被使用的一个或多个标签相关的本地缓存。
  * 标记的资源集可能会在设备上保留一段时间，然后才被清除。

### 5.1 游戏举例

继续使用游戏示例，在分为多个级别的游戏中，用户只需要与用户正在玩的水平和下一个可能级别相关的资源。

***下图显示一个应用，其中包含所有级别的所有资源。***

![adventure\_no\_odr\_2x](/files/-Mg45RzorM4hjDbUedrU)

通过为不同级别以及不需要包含在应用中的其他共享资源创建标签，可以缩小应用包的大小。

***下图显示了一个更小的应用，其标记的资源集托管在\*\*\*\* ****`App Store`**** \*\*\*\*上。***

![adventure\_odr\_2x](/files/-Mg45RzpH1VvAVUsBmEQ)

### 5.2 额外设置

您可以指定

* `应用从应用商店安装时必须加载的标签`
* `为资源请求设置加载优先级`
* `跟踪下载进度`
* 以及`为不再使用的下载标签设置保存优先级`

## 六、 ODR 的生命周期

* 用户首次启动该应用程序时，设备上唯一的按需资源是用于`预匹配的资源`。
* 当用户与应用交互时，应用请求代表一组资源的标签，使用与标签相关的资源，然后通知操作系统它已使用完标签。
* 操作系统会在之后的某个合适的时间，清除一个或多个标签。

> 应用按标签请求，而不是特定的资源。

### 6.1 共享 ODR

当您使用 `ODR` 开发应用时，您可能会注意到请求一个标签会同时下载的其他标签相关的资源。这是因为操作系统与用于下载共享资源的优化资产包配合使用。一个标签可能存在多个 `Asset` 中。构建应用时，`Asset` 由 `Xcode` 生成。

* 一个例子是一个游戏，其中`级别1`和`级别2`标签共享一些资源。`Xcode` 生成三个 `Asset`：
  * Level 1. 只有 `Level1` 标签
  * Level 2. 只有 `Level2` 标签
  * Level 1 + Level 2. 同时有 `Level1` 标签和 `Level2` 标签

### 6.2 流程

![ODR\_flow\_2x](/files/-Mg45RzrEBjeVKyjVmj6)

***1.应用程序从操作系统请求标签。操作系统将所要求的标签转换为包含相关资源的一组资产包。***

在下图中应用请求与 `Level1` 和 `Forest` 标签相关的资源。

![step2\_2x](/files/-Mg45RztvmllGbUtHtzQ)

***2.标签的\*\*\*\* ****`Asset`**** ****位于本地存储中，则生命周期移动到****`第6步`\*\*\*\*。***

***3.标签的一个或多个\*\*\*\* ****`Asset`**** ****托管在**** ****`App Store`**** ****上，原因要么是它是应用的第一次发布，要么是之前加载在设备上的资源被清除（参见下面的**** ****`第9步`****）。***

在下图中，所有 `ODR` 都在应用商店中。

![step1\_2x](/files/-Mg45RzuXwpHiiXIbo03)

***4.操作系统开始下载与尚未存储在本地存储中的相关的资源。***

***5.与所要求的标签相关的\*\*\*\* ****`Asset`**** \*\*\*\*资源完成下载到设备。***

在下图中，与 `Level1` 和 `Forest` 标签相关的资源将下载到了设备中。

![step3\_2x](/files/-Mg45RzvT7vbYETcjycj)

***6.如果成功下载了与所要求的标签相关的资源，或者如果资源已在设备存储中，则操作会增加\*\*\*\* ****`Asset`**** \*\*\*\*的引用计数，并通知应用所请求的标签可用。***

在下图中，应用程序被告知与 `Level1` 和 `Forest` 标签相关的资源是可用的。

![step4\_2x](/files/-Mg45Rzwo6gFnGOAsiNO)

标签可用后，应用将使用与这些标签相关的资源。`ODR` 的访问方式与与下载的应用捆绑的资源相同。下图显示下载的资源作为应用程序的虚拟成员。

![step5\_2x](/files/-Mg45Rzx8sKHijJ11g32)

***7.应用程序通知操作系统，它已经完成了对请求标签的使用。***

***8.操作系统在本地存储中释放标签。这是通过减少与标签相关的\*\*\*\* ****`Asset`**** \*\*\*\*的引用计数来完成的。***

对设备上已使用的标签进行另一个请求将移动到 `第1步`。

***9.操作系统从本地存储中清除与\*\*\*\* ****`Asset`**** \*\*\*\*相关的缓存资源。***

* 当任何请求不再关联标签时，`Asset` 就可以进行清除。与标签相关的资源可能会在设备上保留一段时间，然后才清除，包括跨应用（AppGroup）。
* 对标签提出另一个请求会将生命周期移回 `第1步`。
* 可以通过为标签设置保存优先级来影响清除顺序。

## 七、 总结

本文只是初步的对 `ODR` 有了一个整体初步的了解。很多细节还并没有涉及比如项目怎么配置，代码怎么写。我将在后续文章中继续深入探索。

欢迎点赞关注收藏～

## 参考

* [What is app thinning? (iOS, tvOS, watchOS)](https://help.apple.com/xcode/mac/current/#/devbbdc5ce4f)
