百度APPiOS端包体积优化系列文章的前三篇重点介绍了包体积优化整体方案、图片优化和资源优化,图片优化是从无用图片、AssetCatalog和HEIC格式三个角度做深度优化,资源优化包括大资源优化、无用配置文件和重复资源优化,本文重点介绍代码优化,在百度APP实践中,代码优化包括无用类优化、无用模块瘦身、无用方法瘦身、精简重复代码、工具类瘦身和AB实验固化。在代码优化过程,需要分析Mach-O和LinkMap,在前面的文章我们已经针对Mach-O文件做过了分析,本文先介绍LinkMap文件,然后再详细介绍代码优化方案。
百度APPiOS端包体积优化实践系列文章回顾:
《百度APPiOS端包体积50M优化实践(一)总览》
《百度APPiOS端包体积50M优化实践(二)图片优化》
《百度APPiOS端包体积50M优化实践(三)资源优化》
二、LinkMap文件详解2.1简介LinkMap是Mach-O格式的二进制文件的一种辅助文件,它描述了可执行文件的全貌,包括编译后的每一个目标文件的信息以及它们在可执行文件中的代码段、数据段存储详情。通过LinkMap文件,我们可以知道可执行文件的路径、CPU架构、目标文件、符号等信息,分析可执行文件中哪个类或库占用比较大,进行安装包瘦身,此外,我们可以清楚地了解可执行文件的内部结构和各个目标文件在其中的位置关系,这对于分析和调试非常有帮助。
2.2生成linkMap文件Xcode-Project-BuildSettings-WriteLinkMapFile选项值设为yes,PathtoLinkMapFile设置为指定好的LinkMap文件存储位置。
2.LinkMap文件结构解析2..1基础信息#Path:/Users/richard/Desktop/demo/DerivedData/demo/Build/Products/Debug-iphoneos/demo.app/demo
#Arch:arm64
Path是可执行文件的路径,Arch是架构类型。
2..2Object文件列表Object文件列表列出了所有编译后的目标文件,包括.o文件和dylib库。每个目标文件都有一个对应的编号,上图第一列就是,通过该编号可以对应到具体的类,在后面的Symbols部分,还会用到此编号。
2..2Section段表Section段表描述了各个段在最终编译成的可执行文件中的偏移位置及大小,包括代码段(TEXT)和数据段(DATA)。段表中第一列是数据在文件的偏移位置,第二列是Section占用大小,第三列是Segment类型,第四列是Section类型,关于Segment和Section,在前面文章对于Mach-O详解做过介绍,这儿不再赘述。
2..4SymbolsSymbols模块给出了类里面的方法在内存具体情况。其中
第一列是方法起始地址,通过这个地址我们可以查上面的段表;
第二列是大小,通过这个可以算出方法占用的大小;
第三列是归属的类(.o),值是具体编号,通过反查目标文件列表可以知道对应的类;
第四列是方法名称。
通过Symbols模块我们可以分析出来每个类对应方法的大小。
三、代码优化.1无用类瘦身.1.1静态检测获取无用类方案介绍
所谓的静态检测,就是分析linkmap文件和Mach-o文件,Mach-o文件中__DATA__objc_classlist段记录了所有类的地址,__DATA__objc_classrefs段记录了引用类的地址,取差集可以得到未使用的类的地址,然后进行符号化,就可以得到未被引用的类信息。
第一、获取所有类地址,命令:otool-v-s__DATA__objc_classlist。
otool-v-s__DATA__objc_classlist/Users/ycx/Desktop/demo.app/demoContentsof(__DATA,__objc_classlist)section8298099da489a9800000825ac09b
第二、获取引用类的地址,命令:otool-v-s__DATA__objc_classrefs。
otool-v-s__DATA__objc_classrefs/Users/yangchengxu/Desktop/demo.app/demoContentsof(__DATA,__objc_classrefs)section09000009d000092
第三、取差集,所有类的地址减去引用类的地址,拿到的就是未使用类的地址信息。
第四、符号化,遍历Linkmap和Mach-O文件可获取地址信息对应的具体类名,建立类和地址的映射关系,通过地址反解析出类名。
优缺点
优点:检测方式简单易行。
缺点:
对于存在引用关系但根本不会被调用的类,是无法被判断为无用类的。随着版本迭代,新老员工工作交接,很多功能的入口已经不存在了,相关的类也根本不会被调用,但是引用关系仍然保留。通过静态检测的方式,无法检测出这种情况。
静态检测无法适用于通过反射调用类及方法的场景。因为静态检测无法感知运行时的环境,无法预测哪些类或方法会被反射调用。因此,在这种情况下,静态检测将无法准确地检测出无用类或无用的方法。
.1.2动态检测获取无用类方案介绍
我们知道OC类结构有个isa指针,指向该类的原类meta-class,通过阅读objc源代码,我们发现在meta-class的class_rw_t结构体中的一个flag标志位,flags的位标识当前类在运行时中是否被初始化过,参考源码路径:
Values
for
//Thesearenotemittedbythe