虚拟内存内存可以分为虚拟内存和物理内存 , 其中物理内存是实际占用的内存 , 虚拟内存是在物理内存之上建立的一层逻辑地址 , 保证内存访问安全的同时为应用提供了连续的地址空间 。
物理内存和虚拟内存以页为单位映射 , 但这个映射关系不是一一对应的:一页物理内存可能对应多页虚拟内存;一页虚拟内存也可能不占用物理内存 。

文章插图
iphone 6s 开始 , 物理内存的 Page 大小是 16K , 6 和之前的设备都是 4K , 这是 iPhone 6 相比 6s 启动速度断崖式下降的原因之一 。
mmapmmap 的全称是 memory map , 是一种内存映射技术 , 可以把文件映射到虚拟内存的地址空间里 , 这样就可以像直接操作内存那样来读写文件 。当读取虚拟内存 , 其对应的文件内容在物理内存中不存在的时候 , 会触发一个事件:File Backed Page In , 把对应的文件内容读入物理内存 。
启动的时候 , Mach-O 就是通过 mmap 映射到虚拟内存里的(如下图) 。下图中部分页被标记为 zero fill , 是因为全局变量的初始值往往都是 0 , 那么这些 0 就没必要存储在二进制里 , 增加文件大小 。操作系统会识别出这些页 , 在 Page In 之后对其置为 0 , 这个行为叫做 zero fill 。

文章插图
Page In启动的路径上会触发很多次 Page In , 其实也比较容易理解 , 因为启动的会读写二进制中的很多内容 。Page In 会占去启动耗时的很大一部分 , 我们来看看单个 Page In 的过程:

文章插图
- MMU 找到空闲的物理内存页面
- 触发磁盘 IO , 把数据读入物理内存
- 如果是 TEXT 段的页 , 要进行解密
- 对解密后的页 , 进行签名验证
为什么要解密呢?因为 iTunes Connect 会对上传 Mach-O 的 TEXT 段进行加密 , 防止 IPA 下载下来就直接可以看到代码 。这也就是为什么逆向里会有个概念叫做“砸壳” , 砸的就是这一层 TEXT 段加密 。iOS 13 对这个过程进行了优化 , Page In 的时候不需要解密了 。
二进制重排既然 Page In 耗时 , 有没有什么办法优化呢?启动具有局部性特征 , 即只有少部分函数在启动的时候用到 , 这些函数在二进制中的分布是零散的 , 所以 Page In 读入的数据利用率并不高 。如果我们可以把启动用到的函数排列到二进制的连续区间 , 那么就可以减少 Page In 的次数 , 从而优化启动时间:
以下图为例 , 方法 1 和方法 3 是启动的时候用到的 , 为了执行对应的代码 , 就需要两次 Page In 。假如我们把方法 1 和 3 排列到一起 , 那么只需要一次 Page In , 从而提升启动速度 。

文章插图
链接器 ld 有个参数-order_file 支持按照符号的方式排列二进制 。获取启动时候用到的符号的有很多种方式 , 感兴趣的同学可以看看抖音之前的文章:基于二进制文件重排的解决方案 APP 启动速度提升超 15% 。
IPA 构建pipeline既然要构建 , 那么必然会有一些地方去定义如何构建 , 对应 Xcode 中的两个配置项:
- Build Phase:以 Target 为维度定义了构建的流程 。可以在 Build Phase 中插入脚本 , 来做一些定制化的构建 , 比如 CocoaPod 的拷贝资源就是通过脚本的方式完成的 。
- Build Settings:配置编译和链接相关的参数 。特别要提到的是 other link flags 和 other c flags , 因为编译和链接的参数非常多 , 有些需要手动在这里配置 。很多项目用的 CocoaPod 做的组件化 , 这时候编译选项在对应的.xcconfig 文件里 。
- 源文件(.m/.c/.swift 等)是单独编译的 , 输出对应的目标文件(.o)
- 目标文件和静态库/动态库一起 , 链接出最后的 Mach-O
- Mach-O 会被裁剪 , 去掉一些不必要的信息
推荐阅读
- 抖音外卖平台代理 抖音外卖可以代理吗
- 碧螺春是什么样子的,碧螺春的品质等级
- 安吉白茶鸭的制作方法,安吉白茶的品质特点
- 白牡丹的感官品质,白茶三个等级
- 碧螺春茶的八大功能,碧螺春的品质等级
- 白茶品质评审步骤,天目湖白茶泡水颜色
- 白茶的加工过程是什么,安吉白茶的品质特点
- 冰岛茶的产地与品质,冰岛普洱茶的茶香
- 数据中台建设从数据中台的认知开始
- 曼糯古树茶品质怎么样,曼糯古树赏析
