一、简介
1、Apple对于App可执行文件大小说明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 将在用户设备上占用的磁盘空间大小。
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
3、压缩图片
扫描工具:上述工具稍稍改动下源码,即可扫描所有图片并查看大小信息
压缩工具:ImageOptim 无损压缩
压缩工具:TinyPNG 有损压缩
4、扫描重复文件
// 安装
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分析工具分析
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 | 删除静态链接的可执行文件中未引用的代码 | 是 |