随着项目的不断迭代,我们APP的体积也越来越大,这势必造成下载的资源浪费,同时也给新用户下载带来流量的浪费,因此我们决定开始优化我们的IPA的体积,方便用户下载的同时,也剔除代码中的冗余资源和文件,方便后续的持续迭代。
一:资源部分
1.1 删除项目中无用资源
由于项目随着时间的推移,势必项目中会存在一些资源(图片,视频,音频等等)已经弃用或者被替换,但是没有及时去删除,因此我们首先删除这些无用资源。我们采用LSUnusedResources工具。
注意:必须勾选Ignore similar name 选项,去除相似命名规范的文件,防止这些文件被引用,但是在无任何检查的情况下被删除。
1.2 删除项目中1X的图片资源
iOS适配中,图片资源是展示原则:
1X | 作用于3GS手机,手机的像素320*480,一个点为一个像素 |
---|---|
2X | iPhone4开始,尺寸为320x480,但是像素为640x960,即一个点为2个像素,所有需要2X的图片才可以保证页面不失真 |
3X | iPhone6 plus开始,尺寸为414x736,像素为1242x2208,即一个点3个像素,所以需要3X的图片才能保证页面显示良好 |
作为iPhone4之前的手机,市场占有率基本上可以忽略不计。因此,我们完全有必要删除其中1x的图片资源。
二:压缩资源文件
2.1 压缩图片资源
使用无损压缩,例如 ImageOptim、pngquant命令、tinypng 等工具,如果涉及有损压缩最好要求设计介入进行资源检查。
本次使用ImageOptim对工程中几乎所有的图片做了一次压缩。在压缩过程中,我们发现,大部分图片都能被压缩到原来的70%左右,个别图片能获得更高的压缩比。但是打包测试后,发现IPA包的大小基本上没有任何变化。
最后在查阅了Xcode打包对于图片的资源的处理发现,在打包过程中,有一个步骤叫做compile asset catalog。在这个步骤中,Xcode会自行对png图片作压缩,并且会压缩成能够快速读取渲染的格式。我们的项目经过ImageOptim的压缩后,在compile asset catalog的过程中,Xcode会用自己的算法重新压缩,而这个”重新压缩“的过程,很可能把我们之前的压缩给覆盖。
经过测试,此类压缩基本上对后续的包体积无任何影响,因此查找其他的压缩方案。
2.2 采用Webp压缩图片资源
Webp 是由 Google 推出的图片格式,有损压缩模式下图片体积只有 jpeg 格式的 1/3,无损压缩也能减小 1/4,可以使用 cwebp 进行格式压缩转换,目前 SDWebImage、Kingfisher 都用支持该格式解析的拓展。进过测试发现压缩还是非常有效的。
从项目中随便取2张图片,进行压缩试验:
iOS优化IPA包体积大小
随着项目的不断迭代,我们APP的体积也越来越大,这势必造成下载的资源浪费,同时也给新用户下载带来流量的浪费,因此我们决定开始优化我们的IPA的体积,方便用户下载的同时,也剔除代码中的冗余资源和文件,方便后续的持续迭代。
一:资源部分
1.1 删除项目中无用资源
由于项目随着时间的推移,势必项目中会存在一些资源(图片,视频,音频等等)已经弃用或者被替换,但是没有及时去删除,因此我们首先删除这些无用资源。我们采用LSUnusedResources工具。
注意:必须勾选Ignore similar name 选项,去除相似命名规范的文件,防止这些文件被引用,但是在无任何检查的情况下被删除。
1.2 删除项目中1X的图片资源
iOS适配中,图片资源是展示原则:
1X | 作用于3GS手机,手机的像素320*480,一个点为一个像素 |
---|---|
2X | iPhone4开始,尺寸为320x480,但是像素为640x960,即一个点为2个像素,所有需要2X的图片才可以保证页面不失真 |
3X | iPhone6 plus开始,尺寸为414x736,像素为1242x2208,即一个点3个像素,所以需要3X的图片才能保证页面显示良好 |
作为iPhone4之前的手机,市场占有率基本上可以忽略不计。因此,我们完全有必要删除其中1x的图片资源。
二:压缩资源文件
2.1 压缩图片资源
使用无损压缩,例如 ImageOptim、pngquant命令、tinypng 等工具,如果涉及有损压缩最好要求设计介入进行资源检查。
本次使用ImageOptim对工程中几乎所有的图片做了一次压缩。在压缩过程中,我们发现,大部分图片都能被压缩到原来的70%左右,个别图片能获得更高的压缩比。但是打包测试后,发现IPA包的大小基本上没有任何变化。
最后在查阅了Xcode打包对于图片的资源的处理发现,在打包过程中,有一个步骤叫做compile asset catalog。在这个步骤中,Xcode会自行对png图片作压缩,并且会压缩成能够快速读取渲染的格式。我们的项目经过ImageOptim的压缩后,在compile asset catalog的过程中,Xcode会用自己的算法重新压缩,而这个”重新压缩“的过程,很可能把我们之前的压缩给覆盖。
经过测试,此类压缩基本上对后续的包体积无任何影响,因此查找其他的压缩方案。
2.2 采用Webp压缩图片资源
Webp 是由 Google 推出的图片格式,有损压缩模式下图片体积只有 jpeg 格式的 1/3,无损压缩也能减小 1/4,可以使用 cwebp 进行格式压缩转换,目前 SDWebImage、Kingfisher 都用支持该格式解析的拓展。进过测试发现压缩还是非常有效的。
从项目中随便取2张图片,进行压缩试验:
cwebp -lossless baby_info@3x.png -o ~/Desktop/new.webp
Saving file '/Users/wyz/Desktop/new.webp'
File: baby_info@3x.png 3KB
Dimension: 51 x 51
Output: 2000 bytes (6.15 bpp)
Lossless-ARGB compressed size: 2000 bytes
* Header size: 193 bytes, image data size: 1781
* Precision Bits: histogram=3 transform=3 cache=9
cwebp -lossless baby_info@2x.png -o ~/Desktop/new2.webp
Saving file '/Users/wyz/Desktop/new2.webp'
File: baby_info@2x.png 2KB
Dimension: 34 x 34
Output: 986 bytes (6.82 bpp)
Lossless-ARGB compressed size: 986 bytes
* Header size: 547 bytes, image data size: 414
* Lossless features used: PALETTE
* Precision Bits: histogram=5 transform=5 cache=0
* Palette size: 235
有了这个工具,确实可以大大的压缩项目中的图片资源。我们从上面的代码中看到,3K的图片可以压缩成2K,2K的最后也可以压缩不到1K,确实效率很高。项目中展示压缩过得图片,基本上看不出有任何变化,证明此压缩方案可行。
尝试删除项目中asset catalog除了启动图和icon的图标外的其他图片,经过压缩从10M左右减少到7M左右,所以看起来效果还是不错的,但是项目中图片访问只能通过bundle中来获取,这个和从asset catalog中读取图片的效率就有待商榷,毕竟asset catalog是Apple推荐的图片资源管理模式。
尝试打包在6和plus上安装时发现,安装包大小竟然一样的大小,并且在6上面的大小和优化之前基本上没有什么变化,甚至比之前的还大,显然这不是我们想要的效果。
最后查看App Store的APP在下载过程中发现,Apple会在设备下载APP是,会根据设备类型,下载IPA中不同的资源,即asset catalog中的图片资源,设备支出2X的图片,它也只会选择asset catalog中所有的2X的图片进行下载,所以我们废弃asset catalog使用后,所有的图片都进行了下载,比之前的包大也就说的过去了,因此看起来这种方法也不可取。
但是作为图片压缩一种相当不错的方式,我们完全可以用在网络图片的下载中使用。
2.3 单色图标使用tint color
Apple在iOS7推出的功能,我们可以读取一个图标,然后给它赋予一个color值,在手机屏幕上它就能显示出相应的效果。tint color适用于对单色图标进行着色,相比于其他精简图标的解决方案,tint color方便、可靠、拥有原生支持。
因此,花费一些时间排查一下简单的相同样式图片,采用tint color的方式来填充颜色,从而起到优化IPA包的效果。
2.4 资源考云端下载
Apple从iOS 9开始引入了On Demand Resource功能,即一部分图片可以被放置在苹果的服务器上,不随着app的下载而下载,直到用户真正进入到某个页面时才下载这些资源文件。
但是考虑到项目最低支持的版本,此方法只能放弃。
三:删除重复文件
我们采用fdupes工具。它是通过对比文件的MD5签名,以及逐字节比较文件来识别重复内容,fdupes有各种选项,可以实现对文件的列出、删除、替换为文件副本的硬链接等操作。
文件对比:大小对比 > 部分 MD5 签名对比 > 完整 MD5 签名对比 > 逐字节对比
安装fdupes工具,然后进行测试。
// 进入项目文件下的所有资源文件夹,找出所有重复文件,导出到txt中查看
fdupes -Sr StoryToy > ~/Desktop/123/123.txt
查看项目中的重复文件
386 bytes each:
StoryToy/Bindings/addDevice/RTBindingReadyViewController.h
StoryToy/Bindings/addDevice/RTBindingChoiceListViewController.h
341 bytes each:
StoryToy/RooboMember/VipRights/View/RBVipRightSectionReusableView.h
StoryToy/CloudResource/RooboMember/VipRights/View/RBVipRightSectionReusableView.h
1869 bytes each:
StoryToy/RooboMember/VipRights/View/RBVipRightCollectionViewCell.m
StoryToy/CloudResource/RooboMember/VipRights/View/RBVipRightCollectionViewCell.m
......
可以看出我们确实查到了一些重复文件。
四:编译相关
4.1 Valid Architectures
设置生成IPA包所包含的支持架构,如果项目中还包含32位,armv7及之前的架构,但是我们项目现在已经不支持的可以考虑删除。
4.2 App Thinning
WWDC2015 发布会上首次介绍了 App Thinning,并在 iOS9 开始应用。严格来说App Thinning不会让安装包变小,但用户安装应用时,苹果会根据用户的机型自动选择合适的资源和对应CPU架构的二进制执行文件(也就是说用户本地可执行文件不会同时存在 armv7 和 arm64),减少下载流量和安装占用空间。
五:Framework优化
引入三方Framework时,会包含多个指令集,我们可以手动移除不需要的指令集。或者查找只包含我们项目指令集的framework。
首先我们需要了解不同指令集对应的设备类型:
armv7 | iPhone4s之前,iPad,iPad2,iPad3(The New iPad),iPad mini,iPod Touch 3G |
---|---|
armv7s | iPhone5,iPhone5C,iPad4(iPad with Retina Display) |
arm64 | iPhone5s之后的机型,iPad Air,iPad mini2(iPad mini with Retina Display)等 |
x86_64 | 模拟器64位处理器,Mac应用 |
I386 | 模拟器32位处理器,Mac应用 |
例如我们项目中使用的音频格式转换功能中使用的libopencore-amrnb.a的静态库,他其中包含了所有的指令集,而对于大多数移动端 App,只需要 armv7,armv7s 和 arm64 指令集就足够了。
Architectures in the fat file: /Documents/work/rtoy/ios/RToyAPP/StoryToy/StoryToy/Wechat/RTPlayer/opencore-amr/lib/libopencore-amrnb.a are: i386 x86_64 armv7 armv7s arm64
因此在项目中,我们不需要x86_64,I386 我们只需要保留其他的指令集就可以了。
//查看静态库的信息
lipo -info libopencore-amrnb.a
Architectures in the fat file: libopencore-amrnb.a are: i386 x86_64 armv7 armv7s arm64
// 抽取单一指令集的静态库
lipo libopencore-amrnb.a -thin armv7 -output libopencore-amrnbArmv7
lipo libopencore-amrnb.a -thin armv7s -output libopencore-amrnbArmv7s
lipo libopencore-amrnb.a -thin arm64 -output libopencore-amrnbArm64
// 查看生成的静态库信息
ls
libopencore-amrnb.a libopencore-amrnbArm64 libopencore-amrnbArmv7 libopencore-amrnbArmv7s libopencore-amrwb.a
lipo -info libopencore-amrnbArmv7
Non-fat file: libopencore-amrnbArmv7 is architecture: armv7
// 合成完整的静态库
lipo -create libopencore-amrnbArm64 libopencore-amrnbArmv7 libopencore-amrnbArmv7s -output nb/libopencore-amrnb.a
// 查看新生成的静态库信息
cd nb;ls
libopencore-amrnb.a
lipo -info libopencore-amrnb.a
Architectures in the fat file: libopencore-amrnb.a are: armv7 armv7s arm64
移除多余的指令集后,静态库大小从 4.8MB 降到 3.3MB,效果还是非常完美的。
六:结语
经过本次尝试,IPA包体积都有5%~20%不同程度的减小,当然优化的空间依然巨大。在实际项目瘦身过程中,上面列举的方式不一定都适用。因此对于项目瘦身,还是需要怀着一颗谨慎的心,谨慎检验每一次的尝试。