iOS 包大小优化
官方 App Thinning
iOS9 开始支持。专门针对不同的设备来选择只适用于当前设备的内容以供下载。比如 2x、3x 图片,下载适合自己设备的芯片指令集架构文件。这项工作的大部分有 Xcode 和 App Store 来完成
ming$ du -h Reveal.framework/*
0B Reveal.framework/Headers
0B Reveal.framework/Reveal
16K Reveal.framework/Versions/A/Headers
21M Reveal.framework/Versions/A
21M Reveal.framework/Versions
ming$ file Reveal.framework/Versions/A/Reveal
Reveal.framework/Versions/A/Reveal: Mach-O universal binary with 5 architectures: [i386:current ar archive] [arm64]
Reveal.framework/Versions/A/Reveal (for architecture i386): current ar archive
Reveal.framework/Versions/A/Reveal (for architecture armv7): current ar archive
Reveal.framework/Versions/A/Reveal (for architecture armv7s): current ar archive
Reveal.framework/Versions/A/Reveal (for architecture x86_64): current ar archive
Reveal.framework/Versions/A/Reveal (for architecture arm64): current ar archive
有三方方式:
- App Slicing,会在提交审核后,对 App 做切割,创建不同的变体,这样就可以使用到不同的设备。
- On Demand Resources:按需加载资源是在 App 第一次安装后可下载的资源,多用在游戏根据用户的关卡进度下载资源,过关的资源会被删掉,减小初装 App 包的大小。需要在 Xcode 中设置
Enable On Demand Resources
为是。 - Bitcode:是以编译好的程序的中间表示形式,成为字节码。苹果会对它进行优化,同时有新的架构出来不需要开发者上传新包,可以用字节码直接生成新的架构包。但是由于上传的是中间形态字节码,本地的DSYM无效,需要从App Store 下载对应的DSYM,不然集成的崩溃收集工具无法解析。
无用图片资源
分为 6 步:
- 通过 find 命令获取 App 安装包中的所有资源文件,比如 find /Users/daiming/Project/ -name。
- 设置用到的资源的类型,比如 jpg、gif、png、webp。
- 使用正则匹配在源码中找出使用到的资源名,比如 pattern = @"@"(.+?)""。
- 使用 find 命令找到的所有资源文件,再去掉代码中使用到的资源文件,剩下的就是无用资源了。
- 对于按照规则设置的资源名,我们需要在匹配使用资源的正则表达式里添加相应的规则,比如 @“image_%d”。
- 确认无用资源后,就可以对这些无用资源执行删除操作了。这个删除操作,你可以使用 NSFileManger 系统类提供的功能来完成。
图片资源压缩
WebP
WebP 压缩率高,而且肉眼看不出差异,同时支持有损和无损两种压缩模式。比如,将 Gif 图转为 Animated WebP ,有损压缩模式下可减少 64% 大小,无损压缩模式下可减少 19% 大小。
WebP 支持 Alpha 透明和 24-bit 颜色数,不会像 PNG8 那样因为色彩不够而出现毛边。
cwebp [options] input_file -o output_file.webp
// -lossless 无损压缩
cwebp -lossless original.png -o new.webp
GUI工具 可以实现 PNG 格式转 WebP,同时提供批量处理和记录操作配置的功能
WebP在 CPU 消耗和解码时间上回比 PNG 高两倍,图片大于100kb时可以考虑。
网页工具 tinypng、GUI工具imageoptim进行图片压缩,压缩率没有WebP那么高,不会改变图片压缩方式。
代码瘦身
首先,找出方法和类的全集;
然后,找到使用过的方法和类;
接下来,取二者的差集得到无用代码;
最后,由人工确认无用代码可删除后,进行删除即可。
LinkMap 结合 Mach-O 找无用代码
我们可以通过分析 LinkMap 来获得所有的代码类和方法的信息。获取 LinkMap 可以通过将 Build Setting 里的 Write Link MapFile 设置为 Yes,然后指定 Path to Link Map File 的路径就可以得到每次编译后的 LinkMap 文件了。
LinkMap 文件分为三部分:Object File、Section 和 Symbols。
- Object File 包含了代码工程的所有文件;
- Section 描述了代码段在生成的 Mach-O 里的偏移位置和大小;
- Symbols 会列出每个方法、类、block,以及它们的大小。
iOS 的方法都会通过 objc_msgSend 来调用。而,objc_msgSend 在 Mach-O 文件里是通过__objc_selrefs 这个 section 来获取 selector 这个参数的。
所以,__objc_selrefs
里的方法一定是被调用了的。__objc_classrefs
里是被调用过的类,__objc_superrefs
是调用过 super 的类。通过 __objc_classrefs
和__objc_superrefs
,我们就可以找出使用过的类和子类。
通过 AppCode 找出无用代码
运行时检查类是否真正被使用过
通过 ObjC 的 runtime 源码,我们可以找到怎么判断一个类是否初始化过的函数,如下:
#define RW_INITIALIZED (1<<29)
bool isInitialized() {
return getMeta()->data()->flags & RW_INITIALIZED;
}
isInitialized 的结果会保存到元类的 class_rw_t 结构体的 flags 信息里,flags 的 1<<29 位记录的就是这个类是否初始化了的信息。而 flags 的其他位记录的信息,你可以参看bjc runtime 的源码,如下:
// 类的方法列表已修复
#define RW_METHODIZED (1<<30)
// 类已经初始化了
#define RW_INITIALIZED (1<<29)
// 类在初始化过程中
#define RW_INITIALIZING (1<<28)
// class_rw_t->ro 是 class_ro_t 的堆副本
#define RW_COPIED_RO (1<<27)
// 类分配了内存,但没有注册
#define RW_CONSTRUCTING (1<<26)
// 类分配了内存也注册了
#define RW_CONSTRUCTED (1<<25)
// GC:class 有不安全的 finalize 方法
#define RW_FINALIZE_ON_MAIN_THREAD (1<<24)
// 类的 +load 被调用了
#define RW_LOADED (1<<23)
flags 采用位方式记录布尔值的方式,易于扩展、所用存储空间小、检索性能也好。所以,经常阅读优秀代码,特别有助于提高我们自己的代码质量。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论