iOS 瘦身

一、简介

1、Apple对于App可执行文件大小说明Maximum build file sizes

Maximum build file sizes

2、对于超过200MB(之前是100MB、后来150MB...)的App只能通过WiFi环境下载,无法使用移动网络下载
3、Apple的1MB按1000KB计算
4、可以通过删除资源文件和无用代码等方式减少安装包大小

二、数据维度

1、AppStore中可查看大小信息

App Store Connect -> My Apps -> Activity -> All Builds - Icon
分为Download Size 和 Install Size 两个维度
下载大小:通过 Wi-Fi 下载的压缩 App 大小。
安装大小:此 App 将在用户设备上占用的磁盘空间大小。


App Store Connect.png

2、ipa大小

使用不同的证书打包,ipa大小不同?(待查)

3、资源文件大小

资源文件包括图片、声音、配置文件、文本文件(例如rtf文件)、xib(在安装包中后缀名为nib)、storyboard等

4、可执行文件大小

解包ipa,可查看

5、__TEXT段大小

Apple的限制针对于可执行文件的__TEXT段大小,可使用命令行size+可执行文件查看

6、代码行数

find . -name "*.m" -or -name "*.h" -or -name "*.c" -or -name "*.xib" -or -name "*.storyboard" | xargs wc -l

三、图片资源

1、删除@1X图片

非Retain屏幕使用1X图片,即iPhone 3GS及更早的手机。目前的App可以选择不使用1X图片了,如果老项目中存在,可以删除

2、删除未使用图片

扫描工具:LSUnusedResources

LSUnusedResources

3、压缩图片

扫描工具:上述工具稍稍改动下源码,即可扫描所有图片并查看大小信息
压缩工具:ImageOptim 无损压缩

ImageOptim

压缩工具:TinyPNG 有损压缩
TinyPNG

4、扫描重复文件

fdupes

// 安装
brew install fdupes
// 递归扫描
fdupes -r [dir]

5、扫描相似文件

6、导入方式

Assets.car:在编译时,Images.xcassets中的所有文件会被打包为Assets.car的文件,较直接导入或者Bundle的方式有压缩功能

7、图片本地转网络

On Demand Resources

8、LaunchImage

使用storyboard

9、图片格式

WebP是Google提供的一种图片编码格式,通常情况下WebP格式的图片是原始JPG/PNG图片的1/3,所以对于重度依赖图片显示的应用,转换使用WebP可以节省大量的网络传输数据和时间。对于APP瘦身,使用WebP格式可能是一种方式,可以使用WebP格式的图片替代现有的图片资源,可以一定程度的节省空间

10、On-Demand Resources

四、可执行文件

1、扫描无用代码

1.1、使用AppCode代码静态检查可扫描无用类、方法和引用等
Code->Inspect Code 自定义检查项
1.2、使用Fui扫描
Fui

// 安装fui
gem install fui
// 切换到工程目录
cd ~
// 扫描
fui find

2、扫描相似代码

使用SameCodeFinder扫描相似代码,输出扫描结果中的数字代表两个文件的海明距离,数字越小,相似度越高

// 查看python版本
python --verison
// 安装pip
sudo easy_install pip
// 安装simhash实现
pip install simhash
// 扫描
python SameCodeFinder.py [path] .m

3、LinkMap

1、LinkMap文件是Xcode产生可执行文件的同时生成的链接信息,用来描述可执行文件的构造成分,包括代码段(__TEXT,保存程序代码段编译后的机器码)和数据段(__DATA,保存变量值)的分布情况。
2、在Xcode中修改:
Build Setting - Write Link Map File 修改为YES
Build Setting - Path to Link Map File 为生成文件路径
3、LinkMap文件结构
Path:文件路径
Arch:架构信息
Object files:目标文件列表
Sections:段表,描述各个段在最后编译成的可执行文件中的偏移位置及大小,包括了代码段(__TEXT,保存程序代码段编译后的机器码)和数据段(__DATA,保存变量值)
Symbols:符号表,对 段表 进行了再划分,这里会描述所有的 methods、ivar 和字符串,以及它们对应的地址、大小、文件编号信息
Dead Stripped Symbols

# Path: /Users/kongzhaoyang/Library/Developer/Xcode/DerivedData/jrxz-bgzkyqdtyliqnybabrdnalwennuw/Build/Intermediates.noindex/jrxz.build/Debug-iphoneos/jrxz.build/Objects-normal/arm64/京东惠民
# Arch: arm64
# Object files:
[  0] linker synthesized
[  1] /Users/kongzhaoyang/Library/Developer/Xcode/DerivedData/jrxz-bgzkyqdtyliqnybabrdnalwennuw/Build/Intermediates.noindex/jrxz.build/Debug-iphoneos/jrxz.build/Objects-normal/arm64/JRXZCenterTabBar.o
[  2] /Users/kongzhaoyang/Library/Developer/Xcode/DerivedData/jrxz-bgzkyqdtyliqnybabrdnalwennuw/Build/Intermediates.noindex/jrxz.build/Debug-iphoneos/jrxz.build/Objects-normal/arm64/JRXZTabBarNetworkHandler.o
[  3] /Users/kongzhaoyang/Library/Developer/Xcode/DerivedData/jrxz-bgzkyqdtyliqnybabrdnalwennuw/Build/Intermediates.noindex/jrxz.build/Debug-iphoneos/jrxz.build/Objects-normal/arm64/AppDelegate.o
[  4] /Users/kongzhaoyang/Library/Developer/Xcode/DerivedData/jrxz-bgzkyqdtyliqnybabrdnalwennuw/Build/Intermediates.noindex/jrxz.build/Debug-iphoneos/jrxz.build/Objects-normal/arm64/JRXZTabBarController.o
[  5] /Users/kongzhaoyang/Library/Developer/Xcode/DerivedData/jrxz-bgzkyqdtyliqnybabrdnalwennuw/Build/Intermediates.noindex/jrxz.build/Debug-iphoneos/jrxz.build/Objects-normal/arm64/JRXZTestViewController.o
[  6] /Users/kongzhaoyang/Library/Developer/Xcode/DerivedData/jrxz-bgzkyqdtyliqnybabrdnalwennuw/Build/Intermediates.noindex/jrxz.build/Debug-iphoneos/jrxz.build/Objects-normal/arm64/main.o
[  7] /Users/kongzhaoyang/Library/Developer/Xcode/DerivedData/jrxz-bgzkyqdtyliqnybabrdnalwennuw/Build/Products/Debug-iphoneos/AFNetworking/libAFNetworking.a(AFAutoPurgingImageCache.o)
[  8] /Users/kongzhaoyang/Library/Developer/Xcode/DerivedData/jrxz-bgzkyqdtyliqnybabrdnalwennuw/Build/Products/Debug-iphoneos/AFNetworking/libAFNetworking.a(AFHTTPSessionManager.o)
...

# Sections:
# Address   Size        Segment Section
0x1000061CC 0x0082E224  __TEXT  __text
0x1008343F0 0x000023D0  __TEXT  __stubs
0x1008367C0 0x00002358  __TEXT  __stub_helper
0x100838B18 0x00061FE8  __TEXT  __objc_methname
0x10089AB00 0x0006E94F  __TEXT  __cstring
0x10090944F 0x00008B5B  __TEXT  __objc_classname
0x100911FAA 0x0000D0A3  __TEXT  __objc_methtype
0x10091F050 0x00017CF0  __TEXT  __gcc_except_tab
0x100936D40 0x0001D2DC  __TEXT  __const
0x10095401C 0x00003AE0  __TEXT  __ustring
0x100957AFC 0x000142FC  __TEXT  __unwind_info
0x10096BDF8 0x00000208  __TEXT  __eh_frame
0x10096C000 0x00000B48  __DATA  __got
0x10096CB48 0x000017E0  __DATA  __la_symbol_ptr
0x10096E328 0x00000020  __DATA  __mod_init_func
0x10096E350 0x00021F70  __DATA  __const
0x1009902C0 0x00036080  __DATA  __cfstring
0x1009C6340 0x00002A80  __DATA  __objc_classlist
0x1009C8DC0 0x00000098  __DATA  __objc_nlclslist
0x1009C8E58 0x000001A8  __DATA  __objc_catlist
0x1009C9000 0x00000070  __DATA  __objc_nlcatlist
0x1009C9070 0x00000720  __DATA  __objc_protolist
0x1009C9790 0x00000008  __DATA  __objc_imageinfo
0x1009C9798 0x0018B7B8  __DATA  __objc_const
0x100B54F50 0x00018CC8  __DATA  __objc_selrefs
0x100B6DC18 0x000000E8  __DATA  __objc_protorefs
0x100B6DD00 0x00002A08  __DATA  __objc_classrefs
0x100B70708 0x00001A38  __DATA  __objc_superrefs
0x100B72140 0x000067A0  __DATA  __objc_ivar
0x100B788E0 0x0001A900  __DATA  __objc_data
0x100B931E0 0x00055FF4  __DATA  __data
0x100BE91D8 0x00007EC0  __DATA  __bss
0x100BF1098 0x0000A700  __DATA  __common
# Symbols:
# Address   Size        File  Name
0x1000061CC 0x00000238  [  1] -[JRXZCenterTabBar initWithFrame:]
0x100006404 0x000000BC  [  1] -[JRXZCenterTabBar addBtnDidClick]
0x1000064C0 0x0000050C  [  1] -[JRXZCenterTabBar layoutSubviews]
0x1000069CC 0x0000004C  [  1] _CGRectMake
0x100006A18 0x0000002C  [  1] _CGPointMake
0x100006A44 0x000001F0  [  1] -[JRXZCenterTabBar hitTest:withEvent:]
0x100006C34 0x00000034  [  1] -[JRXZCenterTabBar tabBarDelegate]
0x100006C68 0x00000044  [  1] -[JRXZCenterTabBar setTabBarDelegate:]
0x100006CAC 0x0000002C  [  1] -[JRXZCenterTabBar centerButton]
...

# Dead Stripped Symbols:
#           Size        File  Name
<<dead>>    0x00000007  [  3] literal string: bounds
<<dead>>    0x0000000F  [  3] literal string: initWithFrame:
<<dead>>    0x0000000F  [  3] literal string: sharedInstance
<<dead>>    0x00000005  [  3] literal string: init
<<dead>>    0x00000006  [  3] literal string: class
<<dead>>    0x0000000F  [  3] literal string: isKindOfClass:
<<dead>>    0x00000014  [  3] literal string: respondsToSelector:
<<dead>>    0x0000000E  [  3] literal string: .cxx_destruct
<<dead>>    0x00000008  [  3] literal string: @16@0:8
...

计算某个.o文件在最终安装包中占用的大小,主要是解析目标文件和符号表两个部分,从目标文件读取出每个.o文件名和对应的序号,然后对Symbols中序号相同的文件的Size字段相加,即可得到每个.o文件在最终包的大小。

4、获取LinkMap文件后使用LinkMap分析工具分析

LinkMap

4、Mach-O

1、Mach-O简介

Mach-O为Mach Object文件格式的缩写,是mac上可执行文件的格式,Mach-O文件分为以下几类:
Executable:应用的主要二进制
Dylib Library:动态链接库
Static Library:静态链接库
Bundle:不能被链接的Dylib,只能在运行时使用dlopen( )加载,可当做macOS的插件
Relocatable Object File:可重定向文件类型

2、MachOView

可以使用MachOView进行查看,目前项目无法正常运行,需要改动以下几点:

5、otool

// 动态库
otool -L
// 字符串常量
otool -v -s __TEXT __cstring
// 类名
otool -v -s __TEXT __objc_classname
// 方法名
otool -v -s __TEXT __objc_methname
// 被调用方法名
otool -v -s __DATA __objc_selrefs

6、__TEXT 段迁移

7、引用未使用

需要结合埋点梳理差集

8、压缩方法名长度

9、动态添加属性

10、动态库

11、静态库

lipo thin

12、Architectures

armv7

五、监控

1、业务线
2、资源
3、LinkMap
4、git branch

六、编译选项优化

选项 作用 默认
Optimization Level Release:Fastest, Smalllest 开启那些不增加代码大小的全部优化,并让可执行文件尽可能小
Strip Linked Product YES
Deployment Postprocessing YES
Symbols Hidden by Default YES
Make Strings Read-Only YES
Link-Time Optimization Incremantal
Strip Debug Symbols During Copy YES
Generate Debug Symbols YES
Asset Catalog Compiler - optimization space
Dead Code Stripping YES 删除静态链接的可执行文件中未引用的代码
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容

  • 对于ios来说,由于工程的体量达到一定级别,瘦身是必须的,大的方向分为资源瘦身和代码精简两大方向,当然还有个重要的...
    Arvin_雾里看花阅读 390评论 0 1
  • 经过多个版本的迭代,iOS的ipa包一度达到了153M,突破了150M的阈值,虽然经过了简单的优化,暂时低...
    八月就是八月阅读 3,953评论 1 18
  • 之前每个版本的ipa包打出来都才20M出头,后来不知道从哪个版本起打的包每次都将近40M,但是工程中也没有明显的增...
    雨影阅读 14,027评论 1 10
  • 原文iOS代码瘦身实践 1 分析当前ipa的组成 一般一个ipa会包含: 1) 资源文件 本地文件:数据、配置、数...
    00after阅读 738评论 0 2
  • 2018年11月25日 1:47版本请勿转载作者 zakakekikoku 联系方式同微信 (slow deep ...
    zakakekikoku阅读 205评论 0 0