百度APP iOS端包体积50M优化实践(三) 资源优化
<span style="color: black;"><p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;"><strong style="color: blue;"><strong style="color: blue;">一. 前言</strong></strong></strong></p>
</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">百度APP iOS端包体积优化系列<span style="color: black;">文案</span>的前两篇重点介绍了包体积优化整体<span style="color: black;">方法</span>、各项优化收益和<span style="color: black;">照片</span>优化<span style="color: black;">方法</span>,<span style="color: black;">照片</span>优化是从<span style="color: black;">没</span>用<span style="color: black;">照片</span>、Asset Catalog和HEIC格式三个<span style="color: black;">方向</span>做深度优化。本文重点介绍资源优化,在百度APP实践中,资源优化<span style="color: black;">包含</span>大资源优化、<span style="color: black;">没</span>用配置文件和重复资源优化。不管是资源优化还是代码优化,都<span style="color: black;">必须</span>分析Mach-O文件,以获取资源和代码的引用关系,本文先<span style="color: black;">仔细</span>介绍Mach-O文件。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">百度APP iOS端包体积优化实践系列<span style="color: black;">文案</span>回顾:</span></span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"> 《百度APP iOS端包体积50M优化实践(一)总览》:</span><a style="color: black;">https://mp.weixin.qq.com/s/ANbFzg7X932o-iDpa8</a>FcxQ</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"> 《百度APP iOS端包体积50M优化实践(二) <span style="color: black;">照片</span>优化》:</span><a style="color: black;">https://mp.weixin.qq.com/s/RR7sjhkuTFgUp7S5E8ECMw</a></p><span style="color: black;">
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;"><strong style="color: blue;"><strong style="color: blue;">二. Mach-O文件详解</strong></strong></strong></p>
</span>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;"><strong style="color: blue;">丨</strong></strong></span><span style="color: black;">2.1 简介</span></h3><span style="color: black;">Mach-O为Mach Object文件格式的缩写,用于记录可执行文件、<span style="color: black;">目的</span>代码、动态库和内存转储的文件格式,是运用于Mac以及iOS系统上。</span>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;"><strong style="color: blue;">丨</strong></strong></span><span style="color: black;">2.2 分析Mach-O文件的<span style="color: black;">工具</span></span></h3>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;">丨</strong></span><span style="color: black;">2.2.1 MachOView分析</span></h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">MachOView下载<span style="color: black;">位置</span>: http://sourceforge.net/projects/machoview/</span></span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">MachOView源码<span style="color: black;">位置</span>:https://github.com/gdbinit/MachOView</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">用MachOView能查看MachO文件信息,<span style="color: black;">起步</span>MachOView,在状态栏中点击file,打开MachO文件,如下图所示。</span></span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/Ce6bSqXkduxEOQV5gQlWtyqkOE2TFBPwr2R8ibvQib2GJIlayPoEk7LT5XjdsCJEzy7VMAWTHHKszJjLL9WMIptw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;">丨</strong></span><span style="color: black;">2.2.2 otool命令查看</span></h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;"><span style="color: black;">mac自带otool<span style="color: black;">工具</span>,otool -arch arm64 -ov xxx.app/xxx,可获取所有项目的类结构及定义的<span style="color: black;">办法</span>,示例代码如下所示:</span></span></span></p>
<div style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><span style="color: black;">Contents</span> <span style="color: black;">of (__DATA,__objc_classlist) section</span></span><span style="color: black;"><span style="color: black;">0000000100008238</span> <span style="color: black;">0x100009980</span></span><span style="color: black;"> <span style="color: black;">isa</span> <span style="color: black;">0x1000099a8</span></span><span style="color: black;"> <span style="color: black;">superclass</span> <span style="color: black;">0x0 _OBJC_CLASS_$_UIViewController</span></span><span style="color: black;"> <span style="color: black;">cache</span> <span style="color: black;">0x0 __objc_empty_cache</span></span><span style="color: black;"> <span style="color: black;">vtable</span> <span style="color: black;">0x0</span></span><span style="color: black;"> <span style="color: black;">data</span> <span style="color: black;">0x1000083e8</span></span><span style="color: black;"> <span style="color: black;">flags</span> <span style="color: black;">0x90</span></span><span style="color: black;"> <span style="color: black;">instanceStart</span> <span style="color: black;">8</span></span><span style="color: black;"> <span style="color: black;">instanceSize</span> <span style="color: black;">8</span></span><span style="color: black;"> <span style="color: black;">reserved</span> <span style="color: black;">0x0</span></span><span style="color: black;"> <span style="color: black;">ivarLayout</span> <span style="color: black;">0x0</span></span><span style="color: black;"> <span style="color: black;">name</span> <span style="color: black;">0x100007349 ViewController</span></span><span style="color: black;"> <span style="color: black;">baseMethods</span> <span style="color: black;">0x1000082d8</span></span><span style="color: black;"> <span style="color: black;">entsize</span> <span style="color: black;">24</span></span><span style="color: black;"> <span style="color: black;">count</span> <span style="color: black;">11</span></span><span style="color: black;"> <span style="color: black;">name</span> <span style="color: black;">0x100006424 test4</span></span><span style="color: black;"> <span style="color: black;">types</span> <span style="color: black;">0x1000073e4 v16@0:8</span></span><span style="color: black;"> <span style="color: black;">imp</span> <span style="color: black;">0x100004c58</span></span><span style="color: black;"> <span style="color: black;">name</span> <span style="color: black;">0x1000063b4 viewDidLoad</span></span><span style="color: black;"> <span style="color: black;">*****</span></span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">下面列举otool<span style="color: black;">平常</span>命令:</span></p><span style="color: black;"><strong style="color: blue;">
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">命令</p>
</strong></span><span style="color: black;"><strong style="color: blue;">
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">功能</p>
</strong></span><span style="color: black;">otool -f xxx.app/xxx</span><span style="color: black;">查看fat headers信息</span><span style="color: black;">otool -a xxx.app/xxx</span><span style="color: black;">查看archive header信息</span><span style="color: black;">otool -h xxx.app/xxx</span><span style="color: black;">查看Mach-O头结构</span><span style="color: black;">otool -l xxx.app/xxx</span><span style="color: black;">查看load commands</span><span style="color: black;">otool -L xxx.app/xxx</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">查看依赖的动态库,<span style="color: black;">包含</span>动态</span><span style="color: black;">库名<span style="color: black;">叫作</span>、</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">当前版本号、兼容版本号</span></p><span style="color: black;">otool -t -v xxx.app/xxx</span><span style="color: black;">查看text section </span><span style="color: black;">otool -d xxx.app/xxx</span><span style="color: black;">查看data section</span><span style="color: black;">otool -o xxx.app/xxx</span><span style="color: black;">查看Objective-C segment</span><span style="color: black;">otool -I xxx.app/xxx</span><span style="color: black;">查看symbol table</span><span style="color: black;">otool -v -s __TEXT __cstring</span><span style="color: black;">获取所有静态字符串</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">otool -v -s</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">__TEXT __objc_methname</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">xxx.app/xxx</span></p><span style="color: black;">获取所有<span style="color: black;">办法</span>名<span style="color: black;">叫作</span></span>
</div>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;"><strong style="color: blue;">丨</strong></strong></span><span style="color: black;">2.3 查看文件格式</span></h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">采用file命令<span style="color: black;">能够</span>查看文件格式,lipo -info可查看该Mach-O文件支持的<span style="color: black;">详细</span>CPU架构。</span></p>
<div style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">~ % file /Users/ycx/Desktop/demo.app/demo</span><span style="color: black;">/Users/ycx/Desktop/demo.app/demo: Mach-O <span style="color: black;">64</span>-bit executable arm64</span><span style="color: black;">~ % lipo -info /Users/ycx/Desktop/demo.app/demo</span><span style="color: black;">Non-fat file: <span style="color: black;">/Users/y</span>cx/Desktop/demo.app/demo is architecture: arm64</span></div>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;"><strong style="color: blue;">丨</strong></strong></span><span style="color: black;">2.4 文件结构</span></h3>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;">丨</strong></span><span style="color: black;">2.4.1 总体结构</span></h3><img src="https://mmbiz.qpic.cn/mmbiz_png/Ce6bSqXkduxEOQV5gQlWtyqkOE2TFBPwQZK84JFOFa90diakGvQP30la0gfslYS86RVZmD28LGepSAdgWR7QAhw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;">
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">Mach-O文件<span style="color: black;">重点</span>由三部分<span style="color: black;">构成</span>Header、LoadCommands、Data,在MachO文件的末尾,还有Loader Info信息,<span style="color: black;">暗示</span>可执行文件依赖的字符串表,符号表等信息。</span></p>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;">丨</strong></span><span style="color: black;">2.4.2 Header(头部)</span></h3>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;">丨</strong></span><span style="color: black;">2.4.2.1 数据结构</span></h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">Header(头部): 用于描述当前Mach-O文件的基本信息(CPU类型、文件类型等),XNU代码路径:EXTERNAL_HEADERS/mach-o/loader.h,数据结构如下所示:</span></p><span style="color: black;"><span style="color: black;"><span style="color: black;">struct</span> <span style="color: black;">mach_header_64</span> {</span></span><span style="color: black;"> <span style="color: black;">uint32_t</span> magic; <span style="color: black;">/* mach magic number identifier */</span></span><span style="color: black;"> <span style="color: black;">cpu_type_t</span> cputype; <span style="color: black;">/* cpu specifier */</span></span><span style="color: black;"> <span style="color: black;">cpu_subtype_t</span>cpusubtype;<span style="color: black;">/* machine specifier */</span></span><span style="color: black;"> <span style="color: black;">uint32_t</span> filetype; <span style="color: black;">/* type of file */</span></span><span style="color: black;"> <span style="color: black;">uint32_t</span> ncmds; <span style="color: black;">/* number of load commands */</span></span><span style="color: black;"> <span style="color: black;">uint32_t</span> sizeofcmds; <span style="color: black;">/* the size of all the load commands */</span></span><span style="color: black;"> <span style="color: black;">uint32_t</span> flags; <span style="color: black;">/* flags */</span></span><span style="color: black;"> <span style="color: black;">uint32_t</span> reserved; <span style="color: black;">/* reserved */</span></span><span style="color: black;">};</span>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;">丨</strong>2.4.2.2 查看字段值</h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">命令otool -hv可查看Header<span style="color: black;">每一个</span>字段值。</span></p><span style="color: black;"><span style="color: black;">%</span> <span style="color: black;">otool -hv demo</span></span><span style="color: black;"><span style="color: black;">demo</span>:</span><span style="color: black;"><span style="color: black;">Mach</span> <span style="color: black;">header</span></span><span style="color: black;"> <span style="color: black;">magic</span> <span style="color: black;">cputype cpusubtype caps filetype ncmds sizeofcmds flags</span></span><span style="color: black;"><span style="color: black;">MH_MAGIC_64</span> <span style="color: black;">ARM64 ALL 0x00 EXECUTE 22 3040 NOUNDEFS DYLDLINK TWOLEVEL PIE</span></span><span style="color: black;">用MachOView查看Header数据值:</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/Ce6bSqXkduxEOQV5gQlWtyqkOE2TFBPwFfJAs7hia9TCTslKoCoTbPjAbsN5iceytGr0zNppQv2p41eB7s9YIswg/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;">丨</strong>2.4.2.3 字段<span style="color: black;">详细</span>含义</h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">各个字</span><span style="color: black;">段<span style="color: black;">详细</span></span><span style="color: black;">含义如下所示:</span></p><span style="color: black;"><strong style="color: blue;">字段</strong></span><strong style="color: blue;"><span style="color: black;">说明</span></strong><span style="color: black;">magic</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">魔数头,系统加载器<span style="color: black;">经过</span>该字段快速判断文件类</span><span style="color: black;">型</span></span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">armv7:FEEDFACE</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">arm64:FEEDFACF</span></p><span style="color: black;">cputype</span><span style="color: black;">CPU类型</span><span style="color: black;">cpusubtype</span><span style="color: black;">CPU指定子类型,inter、arm、powerpc等</span><span style="color: black;">filetype</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">说明文件类型(可执行文件、库文件、核心转储文件、内核扩展文件、DYSM文件、动态库等)</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">MH_OBJECT 编译过程中产生的 obj文件</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">MH_EXECUTE 可执行二进制文件 </span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">MH_CORE CoreDump</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">MH_DYLIB 动态库</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">MH_DYLINKER 连接器linker</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">MH_KEXT_BUNDLE 内核扩展文件</span></p><span style="color: black;">ncmds</span><span style="color: black;">加载命令的条数</span><span style="color: black;">sizeofcmds</span><span style="color: black;">加载命令长度</span><span style="color: black;">flags</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">dyld加载时的标志位</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">MH-NOUNDEFS<span style="color: black;">暗示</span>:<span style="color: black;">目的</span><span style="color: black;">无</span>未定义的符号,不存在链接依赖</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">MH-DYLDLINK<span style="color: black;">暗示</span>:该<span style="color: black;">目的</span>文件是dyld的输入文件</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">MH-TWOLEVEL<span style="color: black;">暗示</span>:动态加载二级名<span style="color: black;">叫作</span>空间</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">MH-PIE<span style="color: black;">暗示</span>:<span style="color: black;">位置</span>空间布局随机化</span></p>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;">丨</strong></span><span style="color: black;">2.4.3 LoadCommands(加载命令)</span></h3>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;">丨</strong>2.4.3.1 数据结构</h3><span style="color: black;">LoadCommands(加载命令): 用于描述文件的组织架构和在虚拟内存中的布局方式,告诉操作系统<span style="color: black;">怎样</span>加载Mach-O文件中的数据。XNU代码路径:EXTERNAL_HEADERS/mach-o/loader.h,数据结构如下所示,其中cmd<span style="color: black;">表率</span>加载命令类型,cmdsize<span style="color: black;">表率</span>加载命令<span style="color: black;">体积</span>,在load_command数据结构后面加一个特定结构体信息,<span style="color: black;">区别</span>的cmd类型,结构体<span style="color: black;">亦</span><span style="color: black;">区别</span>。</span><span style="color: black;"><span style="color: black;"><span style="color: black;">struct</span> <span style="color: black;">load_command</span> {</span></span><span style="color: black;"> <span style="color: black;">uint32_t</span> cmd; <span style="color: black;">/* type of load command */</span></span><span style="color: black;"> <span style="color: black;">uint32_t</span> cmdsize; <span style="color: black;">/* total size of command in bytes */</span></span><span style="color: black;">};</span><span style="color: black;"><span style="color: black;">/* Constants for the cmd field of all load commands, the type */</span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span> LC_SEGMENT 0x1 <span style="color: black;">/* segment of this file to be mapped */</span></span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span> LC_SYMTAB 0x2 <span style="color: black;">/* link-edit stab symbol table info */</span></span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span> LC_SYMSEG 0x3 <span style="color: black;">/* link-edit gdb symbol table info (obsolete) */</span></span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span>LC_THREAD 0x4<span style="color: black;">/* thread */</span></span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span> LC_UNIXTHREAD 0x5 <span style="color: black;">/* unix thread (includes a stack) */</span></span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span> LC_LOADFVMLIB 0x6 <span style="color: black;">/* load a specified fixed VM shared library */</span></span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span> LC_IDFVMLIB 0x7 <span style="color: black;">/* fixed VM shared library identification */</span></span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span> LC_IDENT 0x8 <span style="color: black;">/* object identification info (obsolete) */</span></span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span> LC_FVMFILE 0x9 <span style="color: black;">/* fixed VM file inclusion (internal use) */</span></span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span> LC_PREPAGE 0xa <span style="color: black;">/* prepage command (internal use) */</span></span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span> LC_DYSYMTAB 0xb <span style="color: black;">/* dynamic link-edit symbol table info */</span></span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span> LC_LOAD_DYLIB 0xc <span style="color: black;">/* load a dynamically linked shared library */</span></span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span> LC_ID_DYLIB 0xd <span style="color: black;">/* dynamically linked shared lib ident */</span></span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span> LC_LOAD_DYLINKER 0xe <span style="color: black;">/* load a dynamic linker */</span></span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span> LC_ID_DYLINKER 0xf <span style="color: black;">/* dynamic linker identification */</span></span></span><span style="color: black;"><span style="color: black;">#<span style="color: black;">define</span> LC_PREBOUND_DYLIB 0x10 <span style="color: black;">/* modules prebound for a dynamically */</span></span></span><span style="color: black;">*****</span>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;">丨</strong>2.4.3.2 查看字段值</h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">用otool -lv命令<span style="color: black;">能够</span>看到该字段<span style="color: black;">所有</span>信息,如左下图所示,<span style="color: black;">另外</span>,<span style="color: black;">咱们</span><span style="color: black;">亦</span>可用MachOView<span style="color: black;">工具</span>可更直观地观察<span style="color: black;">详细</span>字段,如右下图所示。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/Ce6bSqXkduxEOQV5gQlWtyqkOE2TFBPwE8zzw1wS9WzR91wTD5Wly3NdfyIvZwtM1kWTFqloYDChrjYRnWTssw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;">丨</strong>2.4.3.3 cmd类型及其<span style="color: black;">详细</span><span style="color: black;">功效</span></h3><span style="color: black;"><span style="color: black;"><span style="color: black;">平常</span>的cmd类型及其<span style="color: black;">详细</span><span style="color: black;">功效</span>如下面表格所示:</span></span><strong style="color: blue;"><span style="color: black;">类型</span></strong><strong style="color: blue;"><span style="color: black;"><span style="color: black;">功效</span></span></strong><span style="color: black;">LC_SEGMENT/LC_SEGMENT_64</span><span style="color: black;">将文件中的段映射到进程<span style="color: black;">位置</span>空问中</span><span style="color: black;">LC_DYLD_INFO_ONLY</span><span style="color: black;">动态</span><span style="color: black;">库信</span><span style="color: black;">息,<span style="color: black;">按照</span>该命令是真正动态库绑定,<span style="color: black;">位置</span>重定向<span style="color: black;">要紧</span>的信息</span><span style="color: black;">LC_SYMTAB</span><span style="color: black;">符号表信息</span><span style="color: black;">LC DYSYMTAB</span><span style="color: black;">动态符号表信息</span><span style="color: black;">LC_LOAD_DYLINKER</span><span style="color: black;">加载动态链接器</span><span style="color: black;">LC_UUID</span><span style="color: black;">文件的<span style="color: black;">独一</span>标识,crash解析中<span style="color: black;">亦</span>会有,去匹配dysm文件和crash文件</span><span style="color: black;">LC_VERSION_MIN_IPHONEOS</span><span style="color: black;">二进制文件<span style="color: black;">需求</span>的最低操作系统版本 (iOS Deployment Target)</span><span style="color: black;">LC_MAIN</span><span style="color: black;">程序主线程的入口<span style="color: black;">位置</span></span><span style="color: black;">LC_ENCRYPTION_INFO_64</span><span style="color: black;">加密信息,查看文件<span style="color: black;">是不是</span>加密,<span style="color: black;">倘若</span>已加密<span style="color: black;">必须</span>砸壳</span><span style="color: black;">LC_LOAD_DYLIB</span><span style="color: black;"></span><span style="color: black;">加载的动态库,<span style="color: black;">包含</span>动态库<span style="color: black;">位置</span>和名<span style="color: black;">叫作</span>,当前版本号,兼容版本号</span><span style="color: black;">LC_FUNCTION_STARTS</span><span style="color: black;">函数<span style="color: black;">初始</span><span style="color: black;">位置</span>表</span><span style="color: black;">LC_CODE_SIGNATURE</span><span style="color: black;"></span><span style="color: black;"></span><span style="color: black;">代码签名信息</span>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;">丨</strong>2.4.3.4 LC_SEGMENT_64</h3>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;">丨</strong>2.4.3.4.1 数据结构</h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">在众多cmd命令中,<span style="color: black;">咱们</span><span style="color: black;">必须</span>重点关注的是LC_SEGMENT/LC_SEGMENT_64,LC_SEGMENT是32位,LC_SEGMENT_64是64位,<span style="color: black;">日前</span>主流机型是LC_SEGMENT_64。LC_SEGMENT_64<span style="color: black;">功效</span>是<span style="color: black;">怎样</span>将Data中的各个Segment加载入内存中,而和<span style="color: black;">咱们</span>APP<span style="color: black;">关联</span>的代码及数据,大部分<span style="color: black;">位置于</span>各个Segment中。其数据结构名<span style="color: black;">叫作</span>是segment_command_64,XNU代码路径:EXTERNAL_HEADERS/mach-o/loader.h,源码如下所示:</span></p><span style="color: black;"><span style="color: black;"><span style="color: black;">struct</span> <span style="color: black;">segment_command_64</span> {</span> <span style="color: black;">/* for 64-bit architectures */</span></span><span style="color: black;"> <span style="color: black;">uint32_t</span> cmd; <span style="color: black;">/* LC_SEGMENT_64 */</span></span><span style="color: black;"> <span style="color: black;">uint32_t</span> cmdsize; <span style="color: black;">/* includes sizeof section_64 structs */</span></span><span style="color: black;"> <span style="color: black;">char</span> segname[<span style="color: black;">16</span>]; <span style="color: black;">/* segment name */</span></span><span style="color: black;"> <span style="color: black;">uint64_t</span> vmaddr; <span style="color: black;">/* memory address of this segment */</span></span><span style="color: black;"> <span style="color: black;">uint64_t</span> vmsize; <span style="color: black;">/* memory size of this segment */</span></span><span style="color: black;"> <span style="color: black;">uint64_t</span> fileoff; <span style="color: black;">/* file offset of this segment */</span></span><span style="color: black;"> <span style="color: black;">uint64_t</span> filesize; <span style="color: black;">/* amount to map from the file */</span></span><span style="color: black;"> <span style="color: black;">vm_prot_t</span> maxprot; <span style="color: black;">/* maximum VM protection */</span></span><span style="color: black;"> <span style="color: black;">vm_prot_t</span> initprot; <span style="color: black;">/* initial VM protection */</span></span><span style="color: black;"> <span style="color: black;">uint32_t</span> nsects; <span style="color: black;">/* number of sections in segment */</span></span><span style="color: black;"> <span style="color: black;">uint32_t</span> flags; <span style="color: black;">/* flags */</span></span><span style="color: black;">};</span><span style="color: black;"><strong style="color: blue;">字段</strong></span><span style="color: black;"><span style="color: black;"><strong style="color: blue;">含义</strong></span></span><span style="color: black;">Segment Name</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">Segment名<span style="color: black;">叫作</span></span></p><span style="color: black;">VM Address</span><span style="color: black;">该段被加载后在进程<span style="color: black;">位置</span>空间中的虚拟<span style="color: black;">位置</span></span><span style="color: black;">VM Size</span><span style="color: black;">段的虚拟内存<span style="color: black;">体积</span></span><span style="color: black;">File Offset</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">该段在文件中的偏移</span></p>File Size<span style="color: black;">段在文件中的<span style="color: black;">体积</span></span>maxprot<span style="color: black;">段页面所<span style="color: black;">必须</span>的最高内存<span style="color: black;">守护</span>(可读 可写 可执行)</span><span style="color: black;">initprot</span><span style="color: black;">段页面初始的内存<span style="color: black;">守护</span></span><span style="color: black;">nsects</span><span style="color: black;">段中<span style="color: black;">包括</span>section的数量</span>flags<span style="color: black;">其他标志位</span><span style="color: black;">Mach-O文件有多个段(Segment),<span style="color: black;">每一个</span>段有<span style="color: black;">区别</span>的功能,<span style="color: black;">每一个</span>段又按<span style="color: black;">区别</span>功能划分为多个区(section),四个Segment为__PAGEZERO、__TEXT、_DATA和_LINKEDIT,下面<span style="color: black;">仔细</span>介绍</span>。<h3 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;">丨</strong>2.4.3.4.2 _PAGEZERO</h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/Ce6bSqXkduxEOQV5gQlWtyqkOE2TFBPwiatljGKghGbDibyXUjk1TyEibtiadTrvcCbbNe6SpSgkU3JkHBubmsMic7g/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></p><span style="color: black;">__PAGEZERO Segment是空指针陷阱段,<span style="color: black;">重点</span>是用来<span style="color: black;">捉捕</span>NULL指针的引用,是Mach内核虚拟出来的,是Mach-O加载进内存之后附加的<span style="color: black;">一起</span>区域,maxprot和initprot值都为VM_PROT_NONE,<span style="color: black;">暗示</span>它不可读,不可写,<span style="color: black;">倘若</span><span style="color: black;">拜访</span>__PAGEZERO段,会<span style="color: black;">导致</span>程序崩溃。从上图<span style="color: black;">能够</span><span style="color: black;">发掘</span>,VM Size是4GB,<span style="color: black;">然则</span>真实的File Size<span style="color: black;">体积</span>是0,它只是一个<span style="color: black;">规律</span>上的段,在Data中,<span style="color: black;">基本</span><span style="color: black;">无</span>对应的内容,<span style="color: black;">亦</span><span style="color: black;">无</span>占用任何硬盘空间</span>。<h3 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;">丨</strong>2.4.3.4.3 _TEXT</h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/Ce6bSqXkduxEOQV5gQlWtyqkOE2TFBPw9T1XHYqTHXBazy0QFIHzShWSoGarpwRS6FGgomt7JuJcnbVKxPXhLg/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">__TEXT Segment对应的<span style="color: black;">便是</span>代码段,下图是一张示例截图,其有11个Section,该段对应的内容加载到内存的过程是:从File Offset<span style="color: black;">起始</span>加载<span style="color: black;">体积</span>为File Size的文件,从虚拟<span style="color: black;">位置</span>VM Address<span style="color: black;">起始</span>装填,<span style="color: black;">体积</span><span style="color: black;">亦</span>是VM Size,VM Size跟文件<span style="color: black;">体积</span>File Size是相同的,<span style="color: black;">咱们</span><span style="color: black;">发掘</span>其File Offset为0,在Mach-O文件布局中,__TEXT类型的Segment前面有_PAGEZERO类型的Segment,但_PAGEZERO段的File Offse和File Size为0,<span style="color: black;">因此</span>__TEXT段的File Offset为0。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">maxprot和initprot值都为VM_PROT_READ和VM_PROT_EXECUTE,代码段权限是只读和可执行,防止在内存中被修改</span>。</p>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;">丨</strong>2.4.3.4.4 _DATA</h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p><span style="color: black;">__DATA Segment对应的<span style="color: black;">便是</span>数据段,maxprot和initprot值都为VM_PROT_READ和VM_PROT_WRITE,数据段权限是可读和可写。</span>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><strong style="color: blue;">丨</strong>2.4.3.4.5 _LINKEDIT</h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p><span style="color: black;">__LINKEDIT Segment用于描述链接信息段,指向存放 link 操作必要的数据段</span>。<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;">丨</strong></span><span style="color: black;">2.4.4 Data(数据段)</span></h3><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;">
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">Mach-O的Data部分,其实是真正存储APP二进制数据的<span style="color: black;">地区</span>,前面的header和load command,仅是<span style="color: black;">供给</span>文件的说明以及加载信息的功能。</span></p><span style="color: black;">Data(数据段): <span style="color: black;">重点</span>是代码、数据,<span style="color: black;">包括</span>了Load commands中<span style="color: black;">必须</span>的各个段(Segment)的数据,<span style="color: black;">每一个</span>Segment<span style="color: black;">能够</span>有多个Section,下面列举<span style="color: black;">有些</span><span style="color: black;">平常</span>的 Section。在Data(数据段)中,大写的字符串(如__TEXT)<span style="color: black;">表率</span>的是Segment,小写的字符串(如__objc_methtype)<span style="color: black;">表率</span>的是Section。</span><span style="color: black;">Section</span><span style="color: black;">用途</span><span style="color: black;">__TEXT.__text</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">主程序代码</span></p><span style="color: black;">__TEXT.__cstring</span><span style="color: black;">C 语言字符串</span><span style="color: black;">__TEXT.__const</span><span style="color: black;">const <span style="color: black;">重要</span>字修饰的常量</span><span style="color: black;">__TEXT.__stubs</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">用于 Stub 的占位代码,<span style="color: black;">非常多</span><span style="color: black;">地区</span><span style="color: black;">叫作</span>之为桩代码。</span></p><span style="color: black;">__TEXT.__stubs_helper</span><span style="color: black;">当 Stub <span style="color: black;">没</span>法找到真正的符号<span style="color: black;">位置</span>后的<span style="color: black;">最后</span>指向</span><span style="color: black;">__TEXT.__objc_methname</span><span style="color: black;">Objective-C <span style="color: black;">办法</span>名<span style="color: black;">叫作</span></span><span style="color: black;">__TEXT.__objc_methtype</span><span style="color: black;">Objective-C <span style="color: black;">办法</span>类型</span><span style="color: black;">__TEXT.__objc_classname</span><span style="color: black;">Objective-C 类名<span style="color: black;">叫作</span></span><span style="color: black;">__DATA.__data</span><span style="color: black;">初始化过的可变数据</span><span style="color: black;">__DATA.__la_symbol_ptr</span><span style="color: black;">lazy binding 的指针表,表中的指针一<span style="color: black;">起始</span>都指向 __stub_helper</span><span style="color: black;">__DATA.nl_symbol_ptr</span><span style="color: black;">非 lazy binding 的指针表,<span style="color: black;">每一个</span>表项中的指针都指向一个在装载过程中,被动态链<span style="color: black;">设备</span>搜索完成的符号</span><span style="color: black;">__DATA.__const</span><span style="color: black;"><span style="color: black;">无</span>初始化过的常量</span><span style="color: black;">__DATA.__cfstring</span><span style="color: black;">程序中<span style="color: black;">运用</span>的 Core Foundation 字符串(CFStringRefs)</span><span style="color: black;">__DATA.__bss</span><span style="color: black;">BSS,存放为初始化的全局变量,即常说的静态内存分配</span><span style="color: black;">__DATA.__common</span><span style="color: black;"><span style="color: black;">无</span>初始化过的符号声明</span><span style="color: black;">__DATA.__objc_classlist</span><span style="color: black;">Objective-C 类列表</span><span style="color: black;">__DATA.__objc_protolist</span><span style="color: black;">Objective-C 所有的protocol</span><span style="color: black;">__DATA.__objc_imginfo</span><span style="color: black;">Objective-C 镜像信息</span><span style="color: black;">__DATA.__objc_selfrefs</span><span style="color: black;">Objective-C self 引用</span><span style="color: black;">__DATA.__objc_protorefs</span><span style="color: black;">Objective-C 原型引用</span><span style="color: black;">__DATA.__objc_superrefs</span><span style="color: black;">Objective-C 超类引用</span><span style="color: black;">
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;"><strong style="color: blue;"><strong style="color: blue;">三. 资源优化</strong></strong></strong></p>
</span>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;"><strong style="color: blue;">丨</strong></strong></span><span style="color: black;">3.1 简介</span></h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">做为</span>一个航母级别的APP,百度APP技术栈丰富多样,市面上<span style="color: black;">平常</span>的技术框架都有<span style="color: black;">运用</span>,如Hybrid框架、小程序框架、React Native框架、KMM和端智能。<span style="color: black;">另外</span>,百度APP<span style="color: black;">做为</span>日活过亿的APP,为满足用户<span style="color: black;">繁杂</span>多变的需求,<span style="color: black;">拥有</span>的功能包罗万象,如搜索、Feed、短视频、直播、购物、小说、地图、网盘、美颜、人脸识别、AR库等,<span style="color: black;">引起</span>内置的大块资源(大于40K)就有26M,<span style="color: black;">拥有</span>很大的优化空间,资源优化分为三个部分,分别是大资源优化、<span style="color: black;">没</span>用配置文件和重复资源优化,本章节接下来<span style="color: black;">仔细</span>介绍各个模块的优化<span style="color: black;">方法</span>。</span></p>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;"><strong style="color: blue;">丨</strong></strong></span><span style="color: black;">3.2 大资源优化</span></h3>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;">丨</strong></span><span style="color: black;">3.2.1 获取大资源</span></h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">资源<span style="color: black;">指的是</span>plist、js、css、json、端智能模型文件等,因这些文件和<span style="color: black;">照片</span>在优化方式差异很大,<span style="color: black;">因此</span>把两者区<span style="color: black;">掰开</span>来。获取大资源<span style="color: black;">重点</span>途径是递归遍历ipa包的所有资源,体积大于指定阈值的文件<span style="color: black;">便是</span><span style="color: black;">咱们</span>要针对性优化的大资源,在百度APP优化实践中<span style="color: black;">咱们</span><span style="color: black;">选择</span>了40K<span style="color: black;">做为</span>阈值,参考脚本如下所示:</span></p><span style="color: black;"><span style="color: black;"><span style="color: black;">def</span> <span style="color: black;">findBigResources</span><span style="color: black;">(path,threshold)</span></span>:</span><span style="color: black;"> pathDir = os.listdir(path)</span><span style="color: black;"> <span style="color: black;">for</span> allDir <span style="color: black;">in</span> <span style="color: black;">pathDir:</span></span><span style="color: black;">child = os.path.join(<span style="color: black;">%s%s</span> % (path, allDir))</span><span style="color: black;"> <span style="color: black;">if</span> os.path.isfile(child):</span><span style="color: black;"> <span style="color: black;"># 获取读到的文件的后缀</span></span><span style="color: black;"> <span style="color: black;">end</span> = os.path.splitext(child)[-<span style="color: black;">1</span>]</span><span style="color: black;"> <span style="color: black;"># 过滤掉dylib系统库和asset.car</span></span><span style="color: black;"> <span style="color: black;">if</span> <span style="color: black;">end</span> != <span style="color: black;">".dylib"</span> <span style="color: black;">and</span> <span style="color: black;">end</span> != <span style="color: black;">".car"</span>:</span><span style="color: black;"> temp = os.path.getsize(child)</span><span style="color: black;"> <span style="color: black;"># 转换单位:B -> KB</span></span><span style="color: black;"> fileLen = temp / <span style="color: black;">1024</span></span><span style="color: black;"> <span style="color: black;">if</span> fileLen > <span style="color: black;">threshold:</span></span><span style="color: black;"> <span style="color: black;">#print(end)</span></span><span style="color: black;"> print(child + <span style="color: black;">" length is "</span> + str(fileLen));</span><span style="color: black;"> <span style="color: black;">else:</span></span><span style="color: black;"> <span style="color: black;"># 递归遍历子目录</span></span><span style="color: black;"> child = child + <span style="color: black;">"/"</span></span><span style="color: black;"> findBigResources(child,threshold)</span>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;">丨</strong></span><span style="color: black;">3.2.2 优化<span style="color: black;">办法</span></span></h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">异步下载:只要APP首次<span style="color: black;">起步</span>时不<span style="color: black;">必须</span>加载该资源,<span style="color: black;">或</span>即使首次<span style="color: black;">起步</span><span style="color: black;">必须</span>加载<span style="color: black;">然则</span><span style="color: black;">运用</span>频率不高,<span style="color: black;">那样</span>该资源就<span style="color: black;">能够</span>走异步下载;</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">资源压缩:当APP首次<span style="color: black;">起步</span><span style="color: black;">必须</span>加载且频率较高的<span style="color: black;">状况</span>下,<span style="color: black;">能够</span>对大块资源先进行压缩内置APP,<span style="color: black;">起步</span><span style="color: black;">周期</span>异步线程解压再<span style="color: black;">运用</span>;</span></p>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;">丨</strong></span><span style="color: black;">3.3 <span style="color: black;">没</span>用的配置文件</span></h3>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;">丨</strong></span><span style="color: black;">3.3.1 获取配置文件</span></h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">从ipa包中获取plist、json、txt、xib等配置文件,百度技术<span style="color: black;">方法</span>采用的是排除法,<span style="color: black;">由于</span>实践中<span style="color: black;">发掘</span>配置文件格式千奇百怪,<span style="color: black;">非常多</span>业务模块出于安全<span style="color: black;">思虑</span>自定义<span style="color: black;">各样</span>后缀文件,<span style="color: black;">没</span>法穷举,<span style="color: black;">因此</span>采用了排除法。针对<span style="color: black;">照片</span>资源<span style="color: black;">咱们</span>有专门的优化<span style="color: black;">办法</span>,<span style="color: black;">因此</span><span style="color: black;">首要</span>将png、webp、gif、jpg排除掉,JS&CSS资源是<span style="color: black;">通常</span>HTML加载的,在mach-o文件中TEXT字段静态字符串常量不会有<span style="color: black;">表现</span>,<span style="color: black;">因此</span><span style="color: black;">亦</span><span style="color: black;">必须</span>排除掉,最后获取到的<span style="color: black;">便是</span><span style="color: black;">咱们</span><span style="color: black;">必须</span>的配置文件,参考脚本如下所示:</span></p><span style="color: black;"><span style="color: black;"><span style="color: black;">def</span> <span style="color: black;">findProfileResources</span><span style="color: black;">(path)</span></span>:</span><span style="color: black;"> pathDir = os.listdir(path)</span><span style="color: black;"> <span style="color: black;">for</span> allDir <span style="color: black;">in</span> <span style="color: black;">pathDir:</span></span><span style="color: black;"> child = os.path.join(<span style="color: black;">%s%s</span> % (path, allDir))</span><span style="color: black;"> <span style="color: black;">if</span> os.path.isfile(child):</span><span style="color: black;"> <span style="color: black;"># 获取读到的文件的后缀</span></span><span style="color: black;"> <span style="color: black;">end</span> = os.path.splitext(child)[-<span style="color: black;">1</span>]</span><span style="color: black;"> <span style="color: black;">if</span> <span style="color: black;">end</span> != <span style="color: black;">".dylib"</span> <span style="color: black;">and</span> <span style="color: black;">end</span> != <span style="color: black;">".car"</span> <span style="color: black;">and</span> <span style="color: black;">end</span> != <span style="color: black;">".png"</span> <span style="color: black;">and</span> <span style="color: black;">end</span> != <span style="color: black;">".webp"</span> <span style="color: black;">and</span> <span style="color: black;">end</span> != <span style="color: black;">".gif"</span> <span style="color: black;">and</span> <span style="color: black;">end</span> != <span style="color: black;">".js"</span> <span style="color: black;">and</span> <span style="color: black;">end</span> != <span style="color: black;">".css"</span>:</span><span style="color: black;"> print(child + <span style="color: black;">" 后缀 "</span> + <span style="color: black;">end</span>)</span><span style="color: black;"> <span style="color: black;">else:</span></span><span style="color: black;"> <span style="color: black;"># 递归遍历子目录</span></span><span style="color: black;"> child = child + <span style="color: black;">"/"</span></span><span style="color: black;"> findProfileResources(child)</span>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;">丨</strong></span><span style="color: black;">3.3.2 mach-o文件获取静态字符串常量</span></h3><span style="color: black;"><span style="color: black;">咱们</span>加载配置文件的代码经过编译链接最后都会以字符串形式存储到mach-o文件中,<span style="color: black;">详细</span>是TEXT字段静态字符串常量__cstring中,用otool命令<span style="color: black;">能够</span>获取,参考脚本如下所示:</span><span style="color: black;"> <span style="color: black;">lines</span> = os.popen(<span style="color: black;">/usr/bin/otool -v -s __TEXT __cstring %s</span> % path).readlines()</span>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;">丨</strong></span><span style="color: black;">3.3.3 获取<span style="color: black;">没</span>用配置文件</span></h3><span style="color: black;">前面获取的集合做diff,获取<span style="color: black;">没</span>用配置文件,确认<span style="color: black;">没</span>误后删除以减少包体积。<span style="color: black;">倘若</span>你的资源名是拼接<span style="color: black;">运用</span>的,就<span style="color: black;">没</span>法命中,<span style="color: black;">因此</span>删除资源<span style="color: black;">必定</span>要逐个确认。</span>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;">丨</strong></span><span style="color: black;">3.3.4 JS&CSS<span style="color: black;">没</span>用文件排查</span></h3><span style="color: black;">JS&CSS文件<span style="color: black;">拥有</span>特殊性,OC代码<span style="color: black;">能够</span>引用,HTML文件<span style="color: black;">亦</span><span style="color: black;">能够</span>加载引用,<span style="color: black;">照片</span><span style="color: black;">亦</span>是这种<span style="color: black;">状况</span>,<span style="color: black;">然则</span>上面<span style="color: black;">说到</span>的mach-o文件中TEXT字段只能覆盖OC文件的引用方式,而HTML加载才是主流场景,为此针对这种case百度APP采用跟<span style="color: black;">没</span>用<span style="color: black;">照片</span>检测类似的<span style="color: black;">处理</span><span style="color: black;">方法</span>。</span>
<h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><strong style="color: blue;">丨</strong></span><span style="color: black;">3.4 重复资源优化</span></h3>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">从iPA包中获取所有资源文件,<span style="color: black;">经过</span>MD5判断资源<span style="color: black;">是不是</span>重复,参考脚本如下所示:</span></p><span style="color: black;"> <span style="color: black;"><span style="color: black;">def</span> <span style="color: black;">get_file_library</span><span style="color: black;">(path, file_dict)</span>:</span></span><span style="color: black;"> pathDir = os.listdir(path)</span><span style="color: black;"> <span style="color: black;">for</span>allDir<span style="color: black;">in</span> pathDir:</span><span style="color: black;"> child = os.path.join(<span style="color: black;">%s/%s</span> % (path, allDir))</span><span style="color: black;"> <span style="color: black;">if</span> os.path.isfile(child):</span><span style="color: black;"> md5 = img_to_md5(child)</span><span style="color: black;"> <span style="color: black;"># 将md5存入字典</span></span><span style="color: black;"> key = md5</span><span style="color: black;">file_dict.setdefault(key, []).append(allDir)</span><span style="color: black;"> <span style="color: black;">continue</span></span><span style="color: black;"> get_file_library(child, file_dict)</span><span style="color: black;"><span style="color: black;"><span style="color: black;">def</span> <span style="color: black;">img_to_md5</span><span style="color: black;">(path)</span>:</span></span><span style="color: black;"> fd = open(path, <span style="color: black;">rb</span>)</span><span style="color: black;"> fmd5 = hashlib.md5(fd.read()).hexdigest()</span><span style="color: black;"> fd.close()</span><span style="color: black;"> <span style="color: black;">return</span> fmd5</span><span style="color: black;">
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;"><strong style="color: blue;"><strong style="color: blue;">四. 总结</strong></strong></strong></p>
</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">资源优化是包体积优化的重头戏,优化的过程中影响面可控,<span style="color: black;">因此</span>落地收益比较容易,百度APP经过两个季度的优化落地12M的收益,基本<span style="color: black;">处理</span>存量资源的优化问题,<span style="color: black;">同期</span><span style="color: black;">创立</span>资源<span style="color: black;">运用</span>规范和相应的检测流水线<span style="color: black;">处理</span>增量问题。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">本文对Mach-O文件格式做了系统阐释,并且<span style="color: black;">仔细</span>介绍了百度APP大资源优化、<span style="color: black;">没</span>用配置文件和重复资源优化<span style="color: black;">方法</span>,后续<span style="color: black;">咱们</span>会针对其他优化<span style="color: black;">仔细</span>介绍其原理与实现,敬请期待。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;"><span style="color: black;">参考链接</span></span></h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">、Mach内核介绍:</span><span style="color: black;">https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/KernelProgramming/Mach/Mach.html</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">、《深入解析Mac OS X & iOS操作系统》</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">、XNU源码:</span><span style="color: black;">https://github.com/apple/darwin-xnu</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">、Mach-O介绍:</span><span style="color: black;">https://alexdremov.me/mystery-of-mach-o-object-file-builders/</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">、初识Mach-O文件:</span><span style="color: black;">https://www.jianshu.com/p/81928c705c88</span></p>
“板凳”(第三个回帖的人) 楼主继续加油啊!外链论坛加油! 外贸B2B平台有哪些? 楼主的文章深得我心,表示由衷的感谢! 你的话语如春风拂面,温暖了我的心房,真的很感谢。
页:
[1]