machine:cwduserpython3appthinanalyze-bundlepath-opath/to/log-file-cpath/to/config-file命令释义:工具是用python写的,只支持python3。工具包的名字是appthin,analyze-bundle是包分析子命令。第一个参数可以是IPA文件,也可以是App包,还可以是包含App包的目录。-o参数指定分析报告的生成路径。-c参数指定自定义配置文件,包含自定义文件分类信息(配置文件内容下面说明)。工具输出报告如下所示:
/path/to/ipafile-or-appbundle,包含个文件,总大小:.MB(Bytes)-----------------------------------------------优化建议:重复添加的文件,冗余1.MB(Bytes)文件:filename.gif大小:.KB(Bytes)),被添加2次,分别是/filename.gif/PlugIns/extension-name.appex/filename.gif……---------------图片文件:个,大小:4.MB(Bytes),平均大小:9.KB,阈值:50.KB,可能需要优化:14个.KB(Bytes)/filename.jpq……---------------图片包:5个,大小:35.MB(Bytes),平均大小:7.MB,阈值:1.MB,可能需要优化:3个27.MB(Bytes)/Assets.car……---------------可执行程序:9个,大小:.MB(Bytes),平均大小:11.MB,阈值:0.B,可能需要优化:9个……---------------全部文件列表51.MB(Bytes)8.B(8Bytes)/PkgInfo……报告解读如下:头部信息为IPA文件或者App包的路径,安装包内文件个数,文件总大小。其次是优化建议。第一个建议是重复添加的文件,即在不同目录里,名称相同,大小相同,md5相同的文件。每条文件信息包含大小、相对于安装包的路径。接下来是图片文件的信息,包括图片文件个数,总大小,平均大小,阈值,超过阈值的文件个数等,下面还会列出超过阈值的文件,包括大小、相对路径。后面是各种类型文件的相关信息。最后是全部文件列表。通过报告,我们可以获得包内容的全貌。报告中文件的分类信息,是分析工具从内置的配置文件读取的,格式如下面JSON所示(篇幅有限,例子只包含一个可执行程序的分类信息)。用户可以用“-c”参数指定自定义配置文件覆盖默认分类或者新增分类。
{"file_types":[{"type":"exe","displayname":"可执行程序","exts":[],"size_threshold":0}......]}?根据安装包分析报告,我们可以从重复文件、大文件、图片几个角度进行优化。2.1.1重复文件安装包中一般有两种重复文件,我们需要根据业务实际情况进行优化。第一种,copy到不同可执行程序目录的相同文件。现在的iOS应用,除了主程序外,一般还包含若干扩展程序,每个扩展程序都有自己的独立目录,嵌套在主程序目录里。有时主程序、扩展程序会使用相同的文件,如字体文件、配置文件等。在开发阶段,为了方便,开发人员在Xcode里将这种资源配置到多个可执行程序,编译时这些资源会被copy到相应可执行程序的子目录,造成磁盘空间浪费。如下图所示,a-image
2x.png是一个图片文件,它被添加到CustomKeyboard和MyKeyboard两个可执行程序,这张图片在编译阶段会被copy到主程序目录和键盘程序目录。对于这种资源,我们可以把资源读取代码和资源放到动态库里,供各可执行程序使用,这一点可以结合下文的静态库合并成动态库方案一块实现。例如我们在动态库创建ResourceLoader类,即资源加载类,如下所示:implementationResourceLoader+(NSString*)resourcePath{NSBundle*bundle=[NSBundlebundleForClass:self];NSString*path=[bundlepathForResource:"a-image2x"ofType:"png"];returnpath;}end?各可执行程序用[ResourceLoaderresourcePath]方法读取共享资源。第二种,在同一个可执行程序的不同子目录里,重复的文件。输入法安装包自带了多种按键音文件,它们被放到到不同目录,每个目录都有一个1K大小的配置文件,这些文件完全相同。对于按键音配置文件,经过分析,我们最终没有优化。从报告可以看出,此类文件不多,体积很小,如果优化,不仅需要改动代码,还要改变资源提供方的工作流程,投入的成本很高,收益却很小,因此放弃。2.1.2大文件在输入法优化过程中,我们遇到了下面两种类型的大文件。二进制文件,输入法有9M左右的二进制文件是针对iOS8添加的,高版本系统用不到。我们考虑iOS8用户已经很少,且iOS8系统对键盘扩展支持不友好,一些iOS8系统造成的bug无法解决等多方面因素,经过内部讨论,最终放弃了对iOS8的支持。包大小减少了9M左右。大图片,对于使用频率不高,使用路径比较深的几个大图片,我们联系了服务端的同学,将图片改成了后下载。这项优化包大小减少了1.1M左右。2.1.3图片压缩输入法工程大概有6张图片,4多张用AssetCatalog管理,编译时打包成了Assets.car文件,0多张我们打包成了自定义格式,剩下的直接拷贝到安装包。从分析报告可以看到,图片、Assets.car等文件,总共占了40M左右(这还是经过一轮优化后的数据,优化前总共55M)。一般应用中,图片资源也是较多、体积占比较大的,需要着重、定期优化。定期优化的间隔内,业务方还会不断添加新图片。每次优化时,我们怎么知道哪些图片需要优化,哪些不需要优化,提高优化效率呢?为了解决上述问题,我们自研了一套图片压缩工具。工具的核心是使用缓存目录,缓存目录镜像缓存了源码目录图片的优化结果,即对于每个源图片,在缓存目录的相同相对目录下保存优化结果。优化时,对于每张图片,我们先用缓存进行校验,如果优化过,我们不再优化,否则才真正优化。工具每次优化前从服务器更新缓存数据,优化后再将结果提交。这样,我们就可以定期、快速的优化图片了。下面是优化命令:machine:cwduserpython3appthinoptim-imagepath/to/local-repository--cache-remote-repositoryurl/to/image-cache-repository--cache-pathpath/to/cache--exclude-pathsPods--image-suffixespng--image-size--secret-keyskey1key2--replace-origin-opath/to/log-fileoptim-image是图片压缩子命令,它的第一个参数是本地源码目录,--cache-remote-repository参数指定图片缓存仓库远程地址(我们使用的SVN),--cache-path参数提供本地缓存路径。我们还可以用--exclude-paths参数排除一些目录(如Pods),用--image-suffixes指定需要优化的图片后缀(默认png和jpg),用--image-size参数提供一个阈值,只有大小超过阈值的图片才会优化,用--secret-keys参数指定Tinypng的密钥,--replace-origin参数指定优化后是否替换源图片,-o参数指定优化报告的输出路径。优化报告示例如下:
总文件个数,大小13.MB优化文件个数0,优化前大小0.B,优化后大小0.B优化失败文件个数1,大小31.KB命中缓存数,源文件大小13.MB,缓存文件大小13.MB-----已经优化的文件-----relative-path/to/some-image.png,优化前21.57KB,优化后17.38KB......-----优化失败的文件------relative-path/to/some-image.png,失败原因:Filetypeisnotsupported.(HTTP/Unsupportedmediatype)......?报告第一行是总图片数和图片大小,第二行是优化的文件数和优化前后文件大小,第三行是优化失败的文件个数和大小,第四行是命中缓存的文件个数和缓存大小。紧接着列出所有优化的文件和优化失败的文件。除了png和jpg,输入法工程还有不到10张gif图,我们直接使用docsmall在线工具进行压缩。说完了我们的工具,再补充下真正的图片压缩工具。工具内部使用Tinypng进行压缩,使用的是官方的python接口。Tinypng是一款有损压缩工具,压缩率比较高,图片压缩前后的效果肉眼几乎无法分辨,设计师也认可Tinypng压缩的效果。除了Tinypng,我们还考察了以下压缩方案:ImageOptim,支持无损和有损压缩。无损压缩时,压缩率没有Tinypng高,系统用pngcrush处理后反而可能变大;有损压缩时,压缩参数得自己配置,开发人员不容易配置正确。由于这两个缺点我们没有选用ImageOptim。系统压缩。压缩发生在编译阶段,压缩的文件分为两类,一类是直接copy到安装包的图片,一种是AssetCatalog图片。前一种图片,编译阶段Xcode用copypng命令处理。copypng是一个脚本,脚本内部使用pngcrush压缩图片。注意,这个命令只能处理Xcodebuildphase顶级的图片。对于buildphase目录里的图片,Xcode直接copy目录到安装包,图片是不会被copypng处理的。我们可以通过Xcodebuildsetting控制是否启用pngcrush,如下图所示(Xcode工程首次添加png图片后,buildsettings才会出现这两个选项)。这两个开关建议保持默认打开就好。Assetcatalog图片,Xcode编译阶段用actool将AssetCatalog打包成Asserts.car文件。actool是二进制文件,看不到源码。我们通过Xcode改变Assetcatalog的压缩参数,打包后发现对包体积没什么影响。因此Assetcatalog也保持默认值。2.2、Linkmap分析及优化Linkmap文件是编译过程生成的链接信息文件,每个可执行程序可以生成多个linkmap文件,每种CPU架构对应一个。文件主要描述可执行程序链接的信息,包括链接的符号--方法、属性等,包含的section,以及deadstripped的符号。Linkmap文件不会自动生成,我们需要在buildsettings里打开开并设置输出路径,如下图所示:Linkmap文件的格式大致如下:
#Path:/path/to/executable#Arch:arm64#Objectfiles:[0]linkersynthesized[1]/path/to/object-file......#Sections:#AddressSizeSegmentSection0x0040x00C5B__TEXT__text......#Symbols:#AddressSizeFileName0x0040x90[1]-[ClassNamemethod:]#DeadStrippedSymbols:#SizeFileNamedead0x08[1]8-byte-literal
?
要理解Linkmap文件内容,需要了解Mach-O文件格式,由于篇幅关系,我们不展开描述,大家可以参考这篇文章osx-abi-macho-file-format-reference(