前情提要
团队现在正在维护更新一款之前团队遗留下来的App,后来我们接手之后,又是一顿新功能添加,再加上之前老代码是用OC写的,我们接手之后用的是Swift,所以现在的包体积可想而知啊。。。简直惨不忍睹。
哈哈,323MB,包体积越大,我们的可优化幅度就越大。
之前因为赶新功能的业务代码,所以一直没得空去优化包体积。上周打包给测试,又看到这300多MB的安装包,实在是不能忍了,赶紧着手优化了一波。
方案选取
我们本身的代码优化方面的肯定是自身最清楚的,比如哪些功能是已经不用了,哪些代码是无效代码,哪些图片是无用图片,那这些肯定就是我们优化的第一步。
优化之前也顺便翻了一下业界的优化方案,汲取了一些好的优化思路,pick了一些好用的软件、好用的脚本。下面罗列一些业界的优化方案。虽然,我目前还只是在删除一些无用图片和无用代码,但效果已然显著。
- 无用图片删除
- 无用代码删除
- 图片压缩(无损、有损 两种方案,未实践,效果未知)
- 图片放到云端(自己的云服务器、Apple的On-Demand Resources 两种方案,未实践,效果未知)
- 代码优化(重复方法合并,工作量大)
- Bitcode(似乎只在Appstore有效)
我目前还只是在无用资源的删除阶段,且还未完全完成,但目前看下来效果还是很显著的,先放一张半优化后的包体积截图。优化了接近62MB,当然这跟我们之前没有优化过有关,且我们代码中确实有很多无用功能,现在不用了么,当然就可以删了。
进入实战
第一步 删除无用图片
我们删除无用图片,用LSUnusedResources,筛选条件可以直接用默认的,填入自己的工程路径就可以用了,很方便。
第二步 删除无用代码
删除无用代码,这是一个循序渐进的过程,我们首先要确保的是工程可运行,不能根据我们的经验,知道这部分代码不用了,就直接删了,那样,报错信息会让你奔溃的。
其实,我们首先要删的是根本没有import的文件,推荐一个工具,fui,它可以帮助我们找到并且删除没有import的文件,用命令行工具操作,常用的命令行有两个。我们通过ignore-path来忽略搜索目标路径,一般忽略Pods和我们直接引进来的三方库路径.
fui -x --path=/Users/zsy/LaiPlus --ignore-path=Pods --ignore-path=LaiApp_Swift/AliyunApiClient find
fui -x --path=/Users/zsy/LaiPlus --ignore-path=Pods --ignore-path=LaiApp_Swift/AliyunApiClient delete --perform --prompt
这两个命令,一个是寻找,一个是删除,当然删除的时候最好加上--perform --prompt,这样会有确认提示。就像下图这样,我们通过输入 Y or N 来确认是否删除。
删除这一步,我们需要反复执行,因为某些文件删除之后,又会出现一些新的没有import的文件。
Tips1:这里的删除操作,xib文件不会删除,所以xib文件还需要我们自己手动去删除。 Tips2:storyboard有引用的话,库会认为有引用,所以storyboard需要手动删除。
第三步 删除Project文件
无用代码删除后,我们删的是finder里面的真实文件,但Xcode里面的Project文件还有虚色或者红色文件,直接运行是会报错的。所以我们还要把project的文件也要一并删掉,这里如果手动去删,还是蛮麻烦的,所以我去群里请教了一下,果不其然,真有大佬现场撸了一个简易版本的Ruby脚本,但是需要安装Cocoapods的Xcodeproj,这个简易脚本一开始有点坑到我了,哈哈,也怪我拿来主义,初期脚本长这样。
require 'xcodeproj'
project_path = '/Users/zsy/LaiPlus/LaiApp_OC.xcodeproj'
def remove_reference_from_project(reference, project)
project.targets.each do |target|
target.remove_reference(reference)
end
end
project = Xcodeproj::Project.open(project_path)
project.files.each do |file|
file_path = (file.real_path)
unless File.exists?(file_path)
file.remove_from_project
end
end
project.save
结果把我的Framework都删了,还有之前工程引用的一些tbd文件,当然还有.app文件,误删之后会出现build成功,但不会run程序,虽然可以在executable那里直接指定,但不会自动run了,当时我就预感我把project误删了,重新审视了一遍脚本,project修改回退,自己手动打印了一下,证实了我的猜测,所以修改后的脚本长这样。
require 'xcodeproj'
project_path = '/Users/zsy/LaiPlus/LaiApp_OC.xcodeproj'
def remove_reference_from_project(reference, project)
project.targets.each do |target|
target.remove_reference(reference)
end
end
project = Xcodeproj::Project.open(project_path)
project.files.each do |file|
file_path = (file.real_path)
unless File.exists?(file_path)
suffix_path = File.basename(file_path)
unless suffix_path.include?(".framework") | suffix_path.include?(".app") | suffix_path.include?(".tbd")
file.remove_from_project
end
end
end
project.save
把我们不需要删的文件后缀判断一下,完美删除。
第四步 删除有引用的代码
前面删除完之后,你肯定发现,还是有一些无用代码没删掉,因为互相import了,导致库认为我们有使用,所以我们需要帮库找到最顶层的import的代码,把它删掉,比如:A引用了B,B引用了C,C引用了A,这种初期设计的时候不合理导致的强耦合性,我们必须要自己打破循环,当然接下来就还是fui库来帮我们做之后的删除操作。
反复核对、删除
其实上面的删除操作都是需要反复操作,核对的,无用代码删除之后,应该又会产生一些无用图片,所以回过头来还要再把那些无用图片删掉。第四步的打破循环也是一个体力活,但因为我们的老代码都在一个目录下,所以这个过程也可以接受,主要是可操作。
总结
目前的优化还在继续,且还未进行到图片压缩过程,目测无用代码和无用图片还能优化几十MB,应该能优化到200MB左右,具体能优化到多少,后面还会出文章分享。总结一句,删除无用代码的过程切勿操之过急,Git提交频繁一些没关系,但要确保每一次提交的代码都是可运行的,一点点删,别怕慢,因为有时候一把删的过多,就回不了头了。。。哈哈,别问我为什么,问了我也不说。
画外音:娃和她妈已经睡了,自己躲在餐厅汗流浃背码完剩下的字,各位看官,且看且珍惜。
小声哔哔:要个赞真是不择手段,😂😂😂