符号表优化
尽可能的隐藏符号表:
如果我们写了10个方法,但是并不是全都让外部来调用,就需要把这部分符号隐藏
//暴露符号表 __attribute__((visibility("default")))
//隐藏符号
//不会放到Dynamic Symbol Table 里,所以不会被dyld找到 __attribute__((visibility("hidden")))
参考一下fishhook的图:
由上图可见:_la_symbol ptr 会指向 indirect symbol table , indirect symbol table(只存储了symbol table的索引) 指向symbol table,symbol table(包含string table的指针 和 type、desc等) 指向了string table。
那么我们最终优化的就是 _la_symbol ptr ,也就是说我们设置 了
__attribute__((visibility("hidden")))
,符号表就不会被加载到 _la_symbol里,进而减少符号的体积
xcode提供了一个全局的开关,用来设置符号的默认可见性:
我们可以设置为NO,隐藏符号表,然后对需要暴露的方法设置:
__attribute__((visibility("default")))
动态库和静态库的选择
在静态链接(ld)阶段,动态库会把整个lib复制进mach-o中,这显然不太符合包体积优化的需求。
但是静态库在静态链接阶段,仅把用到的文件link到mach-o中,这一点和动态库区别很大,符合包体积优化需求。
对于静态库,尽量不要使用-Objc (加载所有object c文件)或 all_load(加载所有文件),显然这不太合适,可用-force_load替代。
另外,不选用动态库的另外一个原因是:动态库单独享有一份__TEXT段,而静态库也享有一份_TEXT段。
如果动态库、静态库有些类、方法都是相同的,会在静态库、动态库各有一份__TEXT段,造成不必要的冗余。
下面我写了个动态库:(dylib有单独__TEXT,所以不会和静态库的__TEXT产生符号冲突)
关于xcode配置的一些选项
1、(Levels选项内)Generate Debug Symbols 设置为NO,这个配置选项应该会让你减去小半的体积。注意这个如果设置成NO就不会在断点处停下,
这个选项不会生成DSYM文件,但是不建议这么做,因为DSYM文件是解析crash的重要途径,如:bugly就需要上传DSYM文件。
2、Strip Linked Product:If enabled, the linked product of the build will be stripped(剪裁、拖掉)of symbols when performing deployment postprocessing.
当设置成YES , 会剪裁掉不需要的符号,但前提是把Deployment Postprocessing 设置为 YES 。
3、Strip Style:
- All Symbols - 完全剪裁符号,剪裁效果:强
- Non-Global Symbols - 剪裁掉非全局符号(global symbols供外部符号链接时候使用),剪裁效果:中
- Debugging Symbols - 剪裁掉 debugging 的符号,发布app之后可以选择此项,剪裁效果:弱
注:静态库不要选 All Symbols,因为ld在进行link的时候会用到符号来进行链接。
4、Debug Information Level:当设置成Line tables only , 就只会生成方法名,行号等,但不会包含变量、方法参数等上下文
设置后可以看到包体积的确有少量缩减;
5、Dead Code Stripping ,删除 dead code 符号,项目中无用文件较多时,可以减少体积。
6、Debug Information Format ,debug模式下设置为DWARF即可,release模式下设置为DWARF with dSYM File即可
解释一下DWARF:每个文件被编译成.o 后缀的中间文件,这个文件会包含调试信息,也就是DWARF (debugging with attributed record formats)
补充一点:我们即使生成了dsym文件,发布到 appstore 之后,ipa 也不会包含这个dsym文件,这个文件只给开发者来解析crash日志使用。
下面我们用file命令查看dsym、dwarf文件格式:
% file test.app.dSYM
test.app.dSYM: directory //dsym实际是个文件夹
//我们继续查看 dsym 文件夹里面内容,有一个test文件,他就是dwarf格式的文件
% file test
test: Mach-O 64-bit dSYM companion file arm64 //可以看到这个dwarf文件是一个mach-o文件 ,并且是64位的,支持 arm64架构
再看一下这个dsym文件的格式——没有了__TEXT 和 __DATA段,而多了__DWARF段:
7、编译器优化级别
Build Settings->Optimization Level有几个编译优化选项,release版应该选择Fastest, Smalllest[-Os],这个选项会开启那些不增加代码大小的全部优化,并让可执行文件尽可能小。
arm 架构优化
舍弃架构 armv7、armv7s、i386、x86,保留arm64即可,因为从5s往后都是arm64的
- armv7 用于支持4s和4,4s是2011年11月正式上线,虽然还有小部分人在使用,但是追求包体大小的完全可以舍弃了。
- armv7s 用于iPhone5,基本也可以删除了
- i386 是模拟器架构,也可以删除
- x86 是mac架构,可以删除
检测各个模块占用大小
要想知道优化哪些库,那么我们要先生成link map,然后对每个库进行分析
xcode要开启Write Link Map File,然后指定路径即可
https://github.com/zgzczzw/LinkMapParser(解析格式)
下面是解析完的情况:我们可以针对某些特大的包进行符号表剪裁、资源压缩、arm架构剪裁等
================================================================================
linkmap.txt各模块体积汇总
================================================================================
Creating Result File : BaseLinkMapResult.txt
libavcodec.a 15.09M
WeexSDK 4.95M
opencv2 3.64M
React 3.04M
libavformat.a 1.49M
SwiftProtobuf 1.24M
libmp4v2.a 1.23M
libx264.a 1.18M
libPaymentControl.a 1.17M
libavfilter.a 1.14M
linker synthesized 1.12M
Samyou 1.04M
总体积: 179.32M
================================================================================
图片处理
用 LSUnusedResource 这个软件查找项目中没有用到的图片,然后删除
https://github.com/tinymind/LSUnusedResources/raw/master/Release/LSUnusedResources.app.zip
用 ImageOptim 压缩图片的大小,如果是png图片,可以针对不需要透明的图片剪裁掉他的alpha通道。 ImageOptim是支持这一项的
对于PNG图片来说,文件可能更大,但是解码会相对较快,而Xcode会把 PNG图片进行解码优化之后引入工程。
对于JPEG图片来说,他体积更小,但是解码要消耗更长的时间,因为JPEG解压算法比基于zip的PNG算法更加复杂。
查找冗余的文件
Fui : https://github.com/dblock/fui
注意这里:有些用不到的文件,其中包含+load方法,这种应该注意,因为只要文件参与编译,+load方法就会被调用,不要轻易删除