Apple不断放宽在蜂窝网络下,从AppStore下载App的大小限制,大App们的大小都逼近或超过200MB,甚至突破250MB;html
2013年9月,iOS 7正式版后,蜂窝网络下App下载大小的限制,从 50 MB 提高至 100 MB;linux
2017年9月,iOS11正式版本后,限制从 100 MB 提高至 150 MB,并在2019年5月下旬,将 150 MB"默默"放宽到200MB;git
2019年9月,iOS13正式版本后,直接放开了蜂窝网络下App下载大小的限制,主要流量够用,随便下;github
2020年4月1号了,咱们平常用得比较多的App中,微信/手淘/美团App大小突破250MB;滴滴/抖音/快手App大小突破200MB;支付宝App逼近200MB(190MB+),美团外卖App一股清流,才115MB+。objective-c
根据Apple的审核要求,上传App Store的ipa的可执行文件有大小限制,这里的可执行文件大小不是指二进制(Mach-O)文件大小,而是指二进制(Mach-O)文件中__TEXT
部分的大小。shell
IOS 7版本以前, 二进制文件中全部__TEXT
部分总和不得超过80MB;浏览器
iOS 7.X 至 iOS 8.X,二进制文件中,每一个 Architecture Slice(架构片断)中的__TEXT
部分不得超过60MBbash
iOS 9.0以后,二进制文件中全部__TEXT
部分的总和不超过500 MB;具体可参考最大构建版本文件大小微信
2020年4月1号了,几乎全部的iOS App兼容的最低版本都是iOS 9起步,如:微信/美团/美团外卖iOS App最低支持iOS10,支付宝/手淘/滴滴/抖音/快手iOS App最低支持iOS 9。网络
__TEXT
部分的限制,能够优先业务迭代,有人力的状况下,再去作包瘦身;Mach-O文件
周边的知识:Mach-O文件自己、 分析工具和Link Map File等。Mach-O
格式全称为Mach Object文件格式的缩写,是MacOS或者iOS上可执行的程序格式,相似于Windows上的PE格式 (Portable Executable),linux上的ELF格式 (Executable and Linking Format)。Mach-O文件
的分类有以下5类:
Mach-O文件主要包括三部份内容: Header(头部)、Load Commands(加载命令)、Data(数据区)
Header(头部),指明了 CPU 架构、大小端序、文件类型、Load Commands 个数等一些基本信息,Headers 能帮助校验 Mach-O 合法性和定位文件的运行环境,64位架构为例,Header结构定义以下:
struct mach_header_64 {
uint32_t magic; /* mach magic number identifier 魔数,用于快速确认该文件用于64位仍是32位 */
cpu_type_t cputype; /* cpu specifier,CPU**类型,好比 arm */
cpu_subtype_t cpusubtype; /* machine specifier,对应的具体类型,好比arm6四、armv7 */
uint32_t filetype; /* type of file,文件类型,好比可执行文件、库文件、Dsym文件,demo中是2 `MH_EXECUTE`,表明可执行文件*/
uint32_t ncmds; /* number of load commands 加载命令条数 */
uint32_t sizeofcmds; /* the size of all the load commands 全部加载命令的大小 */
uint32_t flags; /* flags 标志位 */
uint32_t reserved; /* reserved 保留字段 */
};
复制代码
Load Commands(加载命令),包含 Mach-O 里命令类型信息,名称和二进制文件的位置;以64位架构为例,Load Commands结构定义以下:
struct segment_command_64 { /* for 64-bit architectures */
uint32_t cmd; /* cmd是Load commands的类型,LC_SEGMENT_64表明将文件中64位的段映射到进程的地址空间*/
uint32_t cmdsize; /* includes sizeof section_64 structs 表明load command的大小 */
char segname[16]; /* segment name */
uint64_t vmaddr; /* memory address of this segment 段的虚拟内存地址 */
uint64_t vmsize; /* memory size of this segment 段的虚拟内存大小 */
uint64_t fileoff; /* file offset of this segment 段在文件中偏移量 */
uint64_t filesize; /* amount to map from the file 段在文件中的大小 */
vm_prot_t maxprot; /* maximum VM protection */
vm_prot_t initprot; /* initial VM protection */
uint32_t nsects; /* number of sections in segment 标示了Segment中有多少secetion */
uint32_t flags; /* flags */
};
复制代码
和
LC_SEGMENT` 是加载的主要命令, 他们指导内核来设置进程的内存空间;Data(数据区)由Segment 的数据组成,是 Mach-O
中 占比最多的部分,有代码有数据,好比符号表。Data 共三个 Segment:__TEXT
(包含执行代码以及其余只读数据)、__DATA
(程序数据,该段可写)、__LINKEDIT
(包含连接器使用的符号以及其余表)。
其中,__TEXT
和 __DATA
对应一个或多个 Section,__LINKEDIT
没有 Section,须要配合 LC_SYMTAB
来解析 symbol table 和 string table。这些里面是 Mach-O 的主要数据。
以64位架构为例,Section的结构定义以下:
struct section_64 { /* for 64-bit architectures */
char sectname[16]; /* name of this section 好比_text、stubs */
char segname[16]; /* segment this section goes in 该section所属的segment,好比__TEXT*/
uint64_t addr; /* memory address of this section 该section在内存的起始位置 */
uint64_t size; /* size in bytes of this section 该section的大小*/
uint32_t offset; /* file offset of this section 该section的文件偏移*/
uint32_t align; /* section alignment (power of 2) 字节大小对齐*/
uint32_t reloff; /* file offset of relocation entries 重定位入口的文件偏移 */
uint32_t nreloc; /* number of relocation entries 须要重定位的入口数量 */
uint32_t flags; /* flags (section type and attributes) 包含section的type和attributes*/
uint32_t reserved1; /* reserved (for offset or index) */
uint32_t reserved2; /* reserved (for count or sizeof) */
uint32_t reserved3; /* reserved */
};
复制代码
备注:__TEXT
表明的是Segment,小写的__text
表明 Section
FatFile/FatBinary
直译“胖二进制”,是一个由不一样的编译架构的Mach-O产物合成的集合体。一个架构的Mach-O
只能在相同架构的机器或者模拟器上用,为了支持不一样架构须要一个集合体。管理Fat File的工具, 能够查看CPU架构, 提取特定架构,整合和拆分库文件
经常使用的方法以下:
# 【查】看胖二进制支持的CPU架构列表
lipo -info xxxx.a/xxxx.framework/xxxx
# 【拆】从胖二进制中提取特定CPU架构的二进制
lipo lxx.a -thin cpu_type(armv7s/arm64等) -output xx_cpu_type.a
# 【合】整合成Fat文件
lipo -create xxxx1 xxxx2 -output xxxxfat
#【删】移除掉特定的cpu架构的文件
lipo -remove cpu_type(armv7s/arm64等) xxxx -output xxxx
复制代码
经常使用来建立、修改库,从库中提出单个模块。
常使用ar命令解压.a文件,可是若是直接解压第三方SDK的.a文件(如微信SDK),会遇到xxx.a is a fat file (use libtool(1) or lipo(1) and ar(1) on it)
的错误。
这是由于这类.a文件是一个胖二进制,包含了多个CPU架构,须要先使用lipo文件来提取特定的CPU架构的二进制文件,使用以下:
# 拆分出个arm64架构的二进制
lipo xx. a -thin arm64 -output xx_arm64.a
# 解压.a文件
ar -x xx_arm64.a
复制代码
被用于显示二进制目标文件的符号表(display name list (symbol table))
经常使用的方法以下:
# 获得Mach-O中的程序符号表
nm path
# 目标文件的全部符号
nm -nm path
复制代码
用来判断是否包含字符串
经常使用的方法以下:
# 检查是否包含xxx字符串:
grep -r "xxx” path
复制代码
otool
(object file displaying tool),能够对指定目标文件或者库文件以特定的方法解析显示,是分析Mach-O文件的利器。(通常安装了Xcode,默认安装了otool)
otool -h app_name.app/app_name
复制代码
otool -l app_name.app/app_name
复制代码
otool -L app_name.app/app_name
复制代码
otool -l app_name.app/app_name | grep crypt
复制代码
# 获取全部类的地址
otool -v -s __DATA __objc_classlist app_name.app/app_name
# 获取全部引用类的地址
otool -v -s __DATA __objc_classrefs app_name.app/app_name
复制代码
otool
当然方便,可是也可使用MachOView工具来查看Mach-O文件,更加直观,很方便看到 Mach-O文件header、 load commands等信息,具体使用见Mach-O文件浏览器---MachOViewMach-O
文件的class信息;它利用OC语言的Runtime特性,将存储在Mach-O文件中的头文件信息提取出来,并生成对应的.h文件。$HOME/custom-tool/bin
目录下export PATH=$HOME/custom-tool/bin/:$PATH
,而后保存并退出source ~/.bash_profile
;获取ipa
文件,修改后缀名为.zip
,解压后,获取Payload
文件中的app文件;
须要注意的是,从App Store下载的app文件都是通过加密的,可执行文件被加上了一层外壳,class-dump没法直接做用于这样的文件。须要使用其它方式将外壳破坏才能够。
将app文件放到指定目录下,进入该目录,执行以下命令
# 导出Mach-O头文件(头文件内容按名字排序)
class-dump -H Mach-O文件路径 -o 头文件存放目录
复制代码
补充统计文件和文件夹数的命令
# 查看某个文件下的文件个数,包括子文件里的
ls -lR|grep "^-"|wc -l
# 查看某文件下的文件夹的个数,包括子文件夹里的
ls -lR|grep "^d"|wc -l
复制代码
工程->Build Setting->Write Link Map File
为YES,Build后生成Link Map File文件的功能;还能够经过设置Path to Link Map File
,指定Link Map File
存放的路径。Path & Arch:Path是可执行文件的路径,Arch是架构类型。
# Path: /Users/xxx/Library/Developer/Xcode/DerivedData/..../app_name.app/app_name
# Arch: arm64
复制代码
Object Files:生成二进制用到的link单元(包括.o文件和dylib库)的路径和文件编号;经过类编号能够对应到具体的类。在后面的Symbols部分,咱们会用到类编号。
# Object files:
[ 0] linker synthesized
[ 1] /Users/xxxx/Library/Developer/Xcode/DerivedData/..../AppDelegate.o
[ 2] /Users/xxxx/Library/Developer/Xcode/DerivedData/..../main.o
# ...
复制代码
Sections: 记录Mach-O中每一个Segment/section的地址范围。Mach-O中有三类的Segement,Segement划分红了不一样的Section,不一样的Section存储着不一样的信息:Segement主要有三类:__TEXT
、__DATA
和__LINKEDIT
__TEXT
包含 Mach header,被执行的代码和只读常量(如C 字符串),只读可执行__DATA
包含全局变量,静态变量等,可读写__LINKEDIT
包含包含了加载程序的『元数据』,好比函数的名称和地址,只读。# 第一列是Section起始位置,第二列是Section占用内存大小,第三列是Segment类型,第四列是Section类型。
# Sections:
# Address Size Segment Section
0x100002780 0x0129617D __TEXT __text
0x1012988FE 0x000015E4 __TEXT __stubs
# ...
复制代码
Symbols: 按顺序记录每一个符号的地址范围
# Symbols:
// __text代码区
# Address Size File Name
0x100002780 0x00000450 [ 2] -[UIButton(SSEdgeInsets) setImageUpTitleDownWithSpacing:]
0x100002BD0 0x00000070 [ 2] _UIEdgeInsetsMake
# ...
复制代码
Address
肯定分布的区域,如__TEXT段的__text区
(存储着代码),__TEXT段的__objc_methname区
(存储着方法名)、__DATA的__objc_classlist区
(存储全部的类)等;Address
,还能够经过符号表找到对应出具体的方法名Name
(方法名越长,最终占用的内存也越大)File
编号找到代码属于哪一个类;__objc_classlist区
的size值都是8,区域里存储的值都是一个指针,指向了类的虚拟地址。_objc_classname
(全部类名)和__objc_classrefs
(引用到的类)的差集找到未引用的类(未引用的类未必是未使用的类)objc_methname
(全部的方法)和__objc_selrefs
(引用的方法)的差异,找到未引用的方法(未引用的方法未必是未使用的方法)iOS App瘦身小记 -- 基本给出了App瘦身一些建议
PNG图片原理二三事 -- 基本介绍了PNG原理,而后就对App瘦身中图片压缩佛系了
Apple 将 iOS AppStore 下载限制从 150M 提升至 200M