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