百度 APP iOS 端包体积 50M 优化实践 (三) 资源优化
01 前言
百度 APP iOS 端包体积优化系列文章的前两篇重点介绍了包体积优化整体方案、各项优化收益和图片优化方案,图片优化是从无用图片、Asset Catalog 和 HEIC 格式三个角度做深度优化。本文重点介绍资源优化,在百度 APP 实践中,资源优化包括大资源优化、无用配置文件和重复资源优化。不管是资源优化还是代码优化,都需要分析 Mach-O 文件,以获取资源和代码的引用关系,本文先详细介绍 Mach-O 文件。
百度 APP iOS 端包体积优化实践系列文章回顾:
《百度APP iOS端包体积50M优化实践(二) 图片优化》
02 Mach-O 文件详解
2.1 简介
Mach-O 为 Mach Object 文件格式的缩写,用于记录可执行文件、目标代码、动态库和内存转储的文件格式,是运用于 Mac 以及 iOS 系统上。
2.2 分析 Mach-O 文件的工具
2.2.1 MachOView 分析
MachOView 下载地址:http://sourceforge.net/projects/machoview/
MachOView 源码地址:https://github.com/gdbinit/MachOView
用 MachOView 能查看 MachO 文件信息,启动 MachOView,在状态栏中点击 file,打开 MachO 文件,如下图所示。
2.2.2 otool 命令查看
mac 自带 otool 工具,otool -arch arm64 -ov xxx.app/xxx,可获取所有项目的类结构及定义的方法,示例代码如下所示:
下面列举 otool 常见命令:
2.3 查看文件格式
采用 file 命令可以查看文件格式,lipo -info 可查看该 Mach-O 文件支持的具体 CPU 架构。
2.4 文件结构
2.4.1 总体结构
Mach-O 文件主要由三部分组成 Header、LoadCommands、Data,在 MachO 文件的末尾,还有 Loader Info 信息,表示可执行文件依赖的字符串表,符号表等信息。
2.4.2 Header(头部)
2.4.2.1 数据结构
Header(头部): 用于描述当前 Mach-O 文件的基本信息(CPU 类型、文件类型等),XNU 代码路径:EXTERNAL_HEADERS/mach-o/loader.h,数据结构如下所示:
2.4.2.2 查看字段值
命令 otool -hv 可查看 Header 每个字段值。
用 MachOView 查看 Header 数据值:
2.4.2.3 字段具体含义
各个字段具体含义如下所示:
2.4.3 LoadCommands(加载命令)
2.4.3.1 数据结构
LoadCommands(加载命令): 用于描述文件的组织架构和在虚拟内存中的布局方式,告诉操作系统如何加载 Mach-O 文件中的数据。XNU 代码路径:EXTERNAL_HEADERS/mach-o/loader.h,数据结构如下所示,其中 cmd 代表加载命令类型,cmdsize 代表加载命令大小,在 load_command 数据结构后面加一个特定结构体信息,不同的 cmd 类型,结构体也不同。
2.4.3.2 查看字段值
用 otool -lv 命令可以看到该字段全部信息,如左下图所示,此外,我们也可用 MachOView 工具可更直观地观察具体字段,如右下图所示。
2.4.3.3 cmd 类型及其具体作用
常见的 cmd 类型及其具体作用如下面表格所示:
2.4.3.4 LC_SEGMENT_64
2.4.3.4.1 数据结构
在众多 cmd 命令中,我们需要重点关注的是 LC_SEGMENT/LC_SEGMENT_64,LC_SEGMENT 是 32 位,LC_SEGMENT_64 是 64 位,目前主流机型是 LC_SEGMENT_64。LC_SEGMENT_64 作用是如何将 Data 中的各个 Segment 加载入内存中,而和我们 APP 相关的代码及数据,大部分位于各个 Segment 中。其数据结构名称是 segment_command_64,XNU 代码路径:EXTERNAL_HEADERS/mach-o/loader.h,源码如下所示:
Mach-O 文件有多个段(Segment),每个段有不同的功能,每个段又按不同功能划分为多个区(section),四个 Segment 为__PAGEZERO、__TEXT、_DATA 和_LINKEDIT,下面详细介绍。
2.4.3.4.2 _PAGEZERO
__PAGEZERO Segment 是空指针陷阱段,主要是用来捕捉 NULL 指针的引用,是 Mach 内核虚拟出来的,是 Mach-O 加载进内存之后附加的一块区域,maxprot 和 initprot 值都为 VM_PROT_NONE,表示它不可读,不可写,如果访问__PAGEZERO 段,会引起程序崩溃。从上图可以发现,VM Size 是 4GB,但是真实的 File Size 大小是 0,它只是一个逻辑上的段,在 Data 中,根本没有对应的内容,也没有占用任何硬盘空间。
2.4.3.4.3 _TEXT
__TEXT Segment 对应的就是代码段,下图是一张示例截图,其有 11 个 Section,该段对应的内容加载到内存的过程是:从 File Offset 开始加载大小为 File Size 的文件,从虚拟地址 VM Address 开始装填,大小也是 VM Size,VM Size 跟文件大小 File Size 是相同的,我们发现其 File Offset 为 0,在 Mach-O 文件布局中,__TEXT 类型的 Segment 前面有_PAGEZERO 类型的 Segment,但_PAGEZERO 段的 File Offse 和 File Size 为 0,所以__TEXT 段的 File Offset 为 0。
maxprot 和 initprot 值都为 VM_PROT_READ 和 VM_PROT_EXECUTE,代码段权限是只读和可执行,防止在内存中被修改。
2.4.3.4.4 _DATA
__DATA Segment 对应的就是数据段,maxprot 和 initprot 值都为 VM_PROT_READ 和 VM_PROT_WRITE,数据段权限是可读和可写。
2.4.3.4.5 _LINKEDIT
__LINKEDIT Segment 用于描述链接信息段,指向存放 link 操作必要的数据段。
2.4.4 Data(数据段)
Mach-O 的 Data 部分,其实是真正存储 APP 二进制数据的地方,前面的 header 和 load command,仅是提供文件的说明以及加载信息的功能。
Data(数据段): 主要是代码、数据,包含了 Load commands 中需要的各个段(Segment)的数据,每个 Segment 可以有多个 Section,下面列举一些常见的 Section。在 Data(数据段)中,大写的字符串(如__TEXT)代表的是 Segment,小写的字符串(如__objc_methtype)代表的是 Section。
03 资源优化
3.1 简介
作为一个航母级别的 APP,百度 APP 技术栈丰富多样,市面上常见的技术框架都有使用,如 Hybrid 框架、小程序框架、React Native 框架、KMM 和端智能。此外,百度 APP 作为日活过亿的 APP,为满足用户复杂多变的需求,具有的功能包罗万象,如搜索、Feed、短视频、直播、购物、小说、地图、网盘、美颜、人脸识别、AR 库等,导致内置的大块资源(大于 40K)就有 26M,具有很大的优化空间,资源优化分为三个部分,分别是大资源优化、无用配置文件和重复资源优化,本章节接下来详细介绍各个模块的优化方案。
3.2 大资源优化
3.2.1 获取大资源
资源是指 plist、js、css、json、端智能模型文件等,因这些文件和图片在优化方式差异很大,所以把两者区分开来。获取大资源主要途径是递归遍历 ipa 包的所有资源,体积大于指定阈值的文件就是我们要针对性优化的大资源,在百度 APP 优化实践中我们选取了 40K 作为阈值,参考脚本如下所示:
3.2.2 优化方法
异步下载:只要 APP 首次启动时不需要加载该资源,或者即使首次启动需要加载但是使用频率不高,那么该资源就可以走异步下载;
资源压缩:当 APP 首次启动需要加载且频率较高的情况下,可以对大块资源先进行压缩内置 APP,启动阶段异步线程解压再使用;
3.2 无用的配置文件
3.3.1 获取配置文件
从 ipa 包中获取 plist、json、txt、xib 等配置文件,百度技术方案采用的是排除法,因为实践中发现配置文件格式千奇百怪,很多业务模块出于安全考虑自定义各种后缀文件,无法穷举,所以采用了排除法。针对图片资源我们有专门的优化方法,所以首先将 png、webp、gif、jpg 排除掉,JS&CSS 资源是一般 HTML 加载的,在 mach-o 文件中 TEXT 字段静态字符串常量不会有体现,所以也需要排除掉,最后获取到的就是我们需要的配置文件,参考脚本如下所示:
3.3.2 mach-o 文件获取静态字符串常量
我们加载配置文件的代码经过编译链接最后都会以字符串形式存储到 mach-o 文件中,具体是 TEXT 字段静态字符串常量__cstring 中,用 otool 命令可以获取,参考脚本如下所示:
3.3.3 获取无用配置文件
前面获取的集合做 diff,获取无用配置文件,确认无误后删除以减少包体积。如果你的资源名是拼接使用的,就无法命中,所以删除资源一定要逐个确认。
3.3.4 JS&CSS 无用文件排查
JS&CSS 文件具有特殊性,OC 代码可以引用,HTML 文件也可以加载引用,图片也是这种情况,但是上面提到的 mach-o 文件中 TEXT 字段只能覆盖 OC 文件的引用方式,而 HTML 加载才是主流场景,为此针对这种 case 百度 APP 采用跟无用图片检测类似的解决方案。
3.4 重复资源优化
从 iPA 包中获取所有资源文件,通过 MD5 判断资源是否重复,参考脚本如下所示:
04 总结
资源优化是包体积优化的重头戏,优化的过程中影响面可控,所以落地收益比较容易,百度 APP 经过两个季度的优化落地 12M 的收益,基本解决存量资源的优化问题,同时建立资源使用规范和相应的检测流水线解决增量问题。
本文对 Mach-O 文件格式做了系统阐释,并且详细介绍了百度 APP 大资源优化、无用配置文件和重复资源优化方案,后续我们会针对其他优化详细介绍其原理与实现,敬请期待。
—— END——
参考资料:
[1]、Mach 内核介绍:https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/KernelProgramming/Mach/Mach.html
[2]、《深入解析 Mac OS X & iOS 操作系统》
[3]、XNU 源码:https://github.com/apple/darwin-xnu
[4]、Mach-O 介绍:https://alexdremov.me/mystery-of-mach-o-object-file-builders/
[5]、初识 Mach-O 文件:https://www.jianshu.com/p/81928c705c88
推荐阅读:
版权声明: 本文为 InfoQ 作者【百度Geek说】的原创文章。
原文链接:【http://xie.infoq.cn/article/3543a4807a24638feff91241e】。文章转载请联系作者。
评论