包瘦身

App Store 官方规定 App 安装包如果超过 150MB,不可以使 OTA(over-the-air)环境下载,也就是只可以在 WiFi 环境下载。并且 App 包体积过大,对用户更新升级率也会有很大影响。
项目由于素材重复体积过大、无用的资源过多、冗余代码过多、重复造轮子等导致包体积过大,所以应用包的瘦身迫在眉睫。

瘦身方案

  • 资源瘦身
  • 基于编译后的瘦身
  • 基于编译过程的瘦身
  • 代码级别瘦身
资源瘦身

统一素材命名规范,去除重复、无用资源文件,解决名字重复问题;
获取资源文件
设置项目工程中的资源类型(jpg/gif/png/webp等)
正在匹配图片名(匹配编号规则),考虑到(image_%d类型)
集合取差集
删除无用图片(NSFileManager)
对于体积过大的资源可以将资源文件放在服务器上,按需下载。

基于编译后的瘦身
Link Map File

经过编译、链接,最终生成一个可执行文件。经过编译器编译会把每个类生成对应的 .o 文件(目标文件)。链接器会把 .o 文件和动态库链接在一起生成一个 mach-o 文件。Link Map File 就是这样一个记录 mach-o 文件格式相关信息的纯文本,里面记录了可执行文件的路径、CPU 架构、目标文件、符号等信息。

Xcode 开启编译选项 Write Link Map File

XCode -> Project -> Build Settings -> 搜 map -> 把 Write Link Map File选项设为 YES,并指定好 LinkMap 的存储位置。

WechatIMG30.jpeg
WechatIMG31.jpeg
LinkMap 文件格式

Path
Path路径记录的是这个 LinkMap 对应的安装包的地址。
Arch
Arch 指的是这个 LinkMap 对应的架构。
Object files
Object files 是编译后生成的文件列表,比如工程里面的 class 文件都编译成了.o文件,像我们比较熟悉的AppDelegate.o 文件等等。还有引进来的几个库,比如UIKit.tbd。第一列的序号是类的编号,通过该编号可以找到对应的类。
Sections
Section 是各种数据类型所在的内存空间,Section 主要分为两大类,__Text和__DATA。__Text指的是程序代码,__DATA指的是已经初始化的变量等。
Symbols
Symbols 这个单词肯定不陌生,什么 Crash 要有对应的符号表,连接器链接的时候经常找不到 Symbols 等。Symbols简单来说就是类名,变量名,方法名等等符号。
Dead Stripped Symbols

image.png
Mach-O

Mach-O 主要由以下三部分组成:
1、Mach-O 头部(Mach Header)。描述了 Mach-O 的 cpu 架构、文件类型以及加载命令等信息。
2、加载命令(load command)。紧跟在头部之后,这些加载指令清晰地告诉加载器如何处理二进制数据,有些命令是由内核处理的,有些是由动态链接器处理的。在源码中有明显的注释来说明这些是动态连接器处理的。
3、Data。Data 中的每个段(segment)的数据都保存在这里,段的概念与ELF文件中段的概念类似。每个段都有一个或多个 Section,它们存放了具体的数据与代码,主要包含代码、数据,例如符号表,动态符号表等等。

image.png
Sections

每个 Section 包含了 Address、Size、Segment
1、__TEXT 包含 Mach header,被执行的代码和只读常量(如C 字符串),只读可执行。
2、__DATA 包含全局变量,静态变量等,可读写。
3、__LINKEDIT 包含了加载程序的元数据,比如函数的名称和地址,只读。
首列是数据在文件的偏移位置,第二列是这一段占用大小,第三列是段类型,代码段和数据段,第四列是段名称。
每一行的数据都紧跟在上一行后面,如第二行 __stubs 的地址 0x10192D49E 就是第一行 __text 的地址 0x1000037D0 加上 size 0x01929CCD,整个可执行文件大致数据分布就是这样。


image.png
Symbols

1、__text代码区
通过地址 0x1000037D0,可以知道它位于__TEXT段的__text区,这段区域存储着代码,通过符号表,根据地址可以对应出源代码,如 +[IMXEnterpriceViewModel creatEnterpriceViewModel]。通过第三列的类编号,可以知道该代码属于 IMXEnterpriceViewModel 类。
2、__objc_methname方法名区
通过地址 0x101B2D1D8,可以知道它位于__TEXT段的__objc_methname区,这段区域存储着方法名,通过符号表,根据地址可以对应出具体的方法名,如 localizedStringForKey:classBundle:。由上面的信息,可以看出方法名越长,最终占用的内存也越大。
3、__objc_classlist类列表区
__objc_classlist 区的 size 值都是8,区域里存储的值都是一个指针,指向了类的虚拟地址(__objc_data区中地址)。
objc_classdata 结构体中保存了类名,方法名,协议名,ivar 指针和属性对应的地址。通过地址,可以找到对应的段和分

image.png
查找未使用的类和方法
  1. 查找无用 selector
    在 Objctive-C 中,由于它的动态性,它可以通过类名和方法名获取这个类和方法进行调用,所以编译器会把项目里所有 OC 源文件编进可执行文件里,哪怕该类和方法没有被使用到。
    结合 LinkMap 文件的 __TEXT.__text,通过正则表达式([+|-][.+\s(.+)]),我们可以提取当前可执行文件里所有 objc 类方法和实例方法(SelectorsAll)。再使用 otool 命令 otool -v -s __DATA __objc_selrefs 逆向 __DATA.__objc_selrefs 段,提取可执行文件里引用到的方法名(UsedSelectorsAll),我们可以大致分析出 SelectorsAll 里哪些方法是没有被引用的(SelectorsAll-UsedSelectorsAll)。
    注意,系统 API 的 Protocol 可能被列入无用方法名单里,如 UITableViewDelegate 的方法,我们只需要对这些 Protocol 里的方法加入白名单过滤即可。
  2. 查找无用 oc 类
    查找无用 oc 类有两种方式,一种是类似于查找无用资源,通过搜索"[ClassName alloc/new"、"ClassName *"、"[ClassName class]”等关键字在代码里是否出现。另一种是通过 otool 命令逆向 __DATA.__objc_classlist 段和 __DATA.__objc_classrefs 段来获取当前所有 oc 类和被引用的 oc 类,两个集合相减就是无用 oc 类。
分析大文件

在 Symbols 部分,可以把类编号相同的 size 加起来,算出每个类或库占用的大小。在 Object files 部分根据类的编号可以查出对应的类。

https://github.com/huanxsd/LinkMap

https://github.com/ming1016/SMCheckProject

基于编译过程的瘦身
  • 编写 clang 插件,编译过程中将插件作为 clang 参数载入生成中间文件,通过编写工具分析所有的方法有哪些会被调用;
  • 通过 clang 遍历语法树,获取嵌套访问关系,找出没有被调用的代码;

http://kangwang1988.github.io/tech/2016/11/01/validate-ios-api-using-clang-plugin.html

代码级瘦身
image.png
具体实施方案:
  • 去除重复、无用资源文件,解决名字重复问题。
  • 图片使用.xcassets管理
  • 使用tinypng压缩PNG图片。视频可以通过 Final cut 等软件进行分辨率压缩。音频则降低码率即可。
  • icon 使用 iconfont
  • 非必须资源文件可以放到自己服务器上, 但必用资源文件**需要内置到安装包中。
  • 尽可能的去除无用的代码、控制类名、方法名长度、冗余字符串
  • 去掉 armv7 ,可执行文件以及库会减小,即本地 .ipa 也会减小
  • Generate Debug Symbols(调试符号,debug下关掉)、Dead Code Stripping、Apple Clang - Code Generation、strip linked product等编译优化
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249