iOS包体积优化的系列文章,其间包含:

  • iOS 包体积优化1 – 总览
  • iOS 包体积优化2 – 怎么剖析ipa包?
  • iOS 包体积优化3 – 代码管理
  • iOS 包体积优化4 – 资源管理
  • iOS 包体积优化5 – 编译优化
  • iOS 包体积优化6 – 长时刻保护

一. 指令集优化

1. ARM处理器指令集

ARM架构过去称作进阶精简指令集机器(Advanced RISC Machine,更早称作:Acorn RISC Machine),是一个32位精简指令集(RISC)处理器架构,ARM处理器非常适用于移动通讯范畴,符合其首要规划方针:体积小、低功耗、低成本、高功能。ARM指令集是指核算机ARM操作指令系统。

指令集 对应机型
i386 模拟器32位处理器
x86_64 模拟器64位处理器
armv6 iPhone, iPhone 3G, iPod 1G/2G
armv7 iPhone 3GS, iPhone 4, iPhone 4S, iPod 3G/4G/5G, iPad, iPad 2, iPad 3, iPad Mini
armv7s iPhone 5, iPhone 5c, iPad 4
arm64 iPhone X,iPhone 8(Plus),iPhone 7(Plus),iPhone 6(Plus),iPhone 6s(Plus), iPhone 5s, iPad Air(2), Retina iPad Mini(2,3)
arm64e XS/XS Max/XR/ iPhone 11, iPhone 11 pro,iPhone 11 Pro Max,iPhone SE (2nd generation),iPhone 12 mini,iPhone 12,iPhone 12 Pro,iPhone 12 Pro Max,Phone 13 mini,Phone 13,iPhone 13 Pro,iPhone 13 Pro Max
关于Architectures

architectures:n. 建筑;架构(architecture 的复数)。

指定工程支撑的指令集的集合,假如设置多个architecture,则生成的二进制数据包会包含多个指令集代码,体积会变大。

Xcode 14的 Release Notes中提到: Building iOS projects with deployment targets for the armv7, armv7s, and i386 architectures is no longer supported. (92831716)。

不再支撑构建 armv7、armv7s 以及 i386 架构的 iOS 项目。

Xcode 14 更新阐明文档

2. Excluded Architectures

Build Settings -> Architectures -> Excluded Architectures

结合本身项目支撑情况,项目中不需求支撑armv7s和armv7。

iOS 包体积优化5 - 编译优化

该编译项在 Release 下面添加装备项目
  • Any iOS SDK 设置为armv7 / &armv7s
  • `Any iOS Simulator SDK,设置为arm64

这个选项的意思是Release形式下针对真机armv7armv7s指令集排除,针对模拟器把arm64排除(模拟器不支撑arm架构)。

二. Build Active Architecture Only装备

Build Settings -> Architectures -> Build Active Architecture Only

该编译项用于设置是否只编译当时运用的设备(连线设备)对应的arm指令集

iOS 包体积优化5 - 编译优化

该编译选项设置为
  • Debug 形式设置为 Yes
  • Release 形式设置为 No

三. Optimization Level

优首要用来在 二进制巨细运转时功能 做取舍。

1. OC 编译最佳优化

Xcode -> Build Setting -> Apple Clang – Code Generation -> Optimization Level

Xcode 是运用 Clang 来编译 Objective-C 言语,咱们的 IDE-Xcode 供给给咱们 6 个等级的编译选项(官方阐明),用来设置生成的代码在速度和二进制巨细方面的优化程度。

iOS 包体积优化5 - 编译优化

优化选项 阐明
None[-O0] 【默许在 Debug 形式下敞开】运用此设置,编译器的方针是削减编译的成本,并使调试发生预期的成果,不会优化代码,供给更快的编译速度和更多的调试信息,
Fast[-O,O1] 【会优化代码功能而且最小限度影响编译时刻,会占用更多的内存】关于大型函数,优化编译需求更多的时刻和更多的内存。-O, -O1经过此设置,编译器试图削减代码巨细和履行时刻,而不履行任何需求很多编译时刻的优化。
Faster[-O2] 【敞开不依赖空间/时刻折衷一切优化选项,会添加编译时刻而且进步代码履行功率】编译器履行几乎一切不涉及空间速度权衡的支撑优化。在此设置下,编译器不履行循环展开、函数内联或寄存器重命名。与Fast设置比较,该设置添加了编译时刻和生成代码的功能。
Fastest[-O3] 【不推荐运用此形式】编译器会敞开一切的优化选项来提高代码履行功率。此形式编译器会履行函数内联使得生成的可履行文件会变得更大。
Fastest Smallest[-Os] 【功能和巨细平衡较好,默许在 Release 形式下敞开】编译器会敞开除了会明显添加包巨细以外的一切优化选项。默许-Os是功能和巨细平衡比较好的
Smallest,Aggressive Size Optimization[-Oz] 【最小的、急进的巨细优化】Oz是Xcode 11之后才呈现的编译优化选项,中心原理是对重复的连续机器指令外联成函数进行复用,因而敞开Oz,能削减二进制的巨细,但一起会带来履行功率但额定耗费。
Fastest, Aggressive Optimization[-Ofast] 【不推荐运用此形式】发动 -O3 中的一切优化,或许会敞开一些违反言语标准的一些优化选项。

空间/时刻折衷

在核算机科学中,时空时内存权衡是经过运用更多的存储空间(或内存)或经过花费很长时刻在极小的空间中处理问题或核算的一种方法。大多数核算机有很多的空间,但不是无限的空间。此外,大多数人愿意等待一段时刻进行大核算,但不是永远。因而,假如您的问题需求很长时刻,但内存不多,时空权衡将允许您运用更多内存并更快地处理问题。或许,假如它能够很快处理,但需求比你更多的内存,你能够试着花更多的时刻在有限的内存中处理问题。

在不同的选项对应的编译速度和二进制文件巨细变化趋势

iOS 包体积优化5 - 编译优化

Optimization Level默许是-Os-OzXcode 11新增的编译优化选项,该设置经过将重复的代码形式隔离到编译器生成的函数中来完成额定的尺寸节约。

该编译选项设置为
  • Debug 形式设置为[-O0]
  • Release 形式设置为 [-Oz] 或许[-Os]; (依据实际情况 挑选更偏功能仍是包巨细)

2. Swift 编译最佳优化

Xcode -> Build Setting -> Swift Compiler – Code Generation -> Optimization Level

Swift 言语的编译器是 swiftlang,一起也是基于 LLVM 后端的。Xcode 9.3 版本之后 Swift 编译器会供给新的选项来帮助削减 Swift 可履行文件的巨细. 咱们的 IDE-Xcode 供给给咱们 3 个等级的编译选项

iOS 包体积优化5 - 编译优化

优化选项 阐明
No optimization[-Onone] 不进行优化,能确保较快的编译速度
Optimize for Speed[-O] 编译器将会对代码的履行功率进行优化,一定程度上会添加包巨细
Optimize for Size[-Osize] 编译器会尽或许削减包的巨细而且最小限度影响代码的履行功率。依据项目不同,大致能够优化掉 5% – 30% 的代码空间占用。 比较 -0 来说,会丢失大约 5% 的运转时功能。

-Osize 依据项目不同,大致能够优化掉 5% – 30% 的代码空间占用。 比较 -0 来说,会丢失大约 5% 的运转时功能。 假如你的项目对运转速度不是特别灵敏,而且能够接受轻微的功能丢失,那么 -Osize 就值得一用。

该编译选项设置为
  • Debug 形式设置为[-Onone]
  • Release 形式设置为 [-Osize]

四. 编译形式 Compilation Mode

Xcode -> Build Setting -> Swift Compiler – Code Generation -> Compilation Mode

编译形式设置,9.3版本之后能够独立设置了。

iOS 包体积优化5 - 编译优化

Incremental(增量)仅编译已修正的文件.

长处:

  • 在进行增量编译时,编译器不必从头编译整个项目,而只能从头编译已更改的文件或依赖已更改的文件
  • 编译器每个文件运转一个实例,因而在具有多个内核的核算机上,它能够编译得更快

缺陷:

  • 假如正在优化的内容跨越多个文件,则不会履行一些优化
  • 编译器确实必须从其他文件中获取一些信息,因而它或许会重复此作业超越必要的次数(假如6个文件引用另一个文件,则在只需求1个文件时,该文件或许会对它履行6次一些作业)
Whole Module(全量) 不考虑修正而构建项目中一切文件。

长处:

  • 这将履行快速编译器能够履行的最大优化
  • 与单文件优化比较,履行更少的冗余作业

缺陷:

  • 这只会运用一个CPU内核来运转代码上一切快速的优化。这意味着多核核算机将无法充分利用编译您的代码
  • 在增量编译中,您的整个模块仍然需求每次都从头编译
该编译选项设置为
  • Debug 形式设置为Incremental
  • Release 形式设置为Whole Module

Xcode 10+的默许设置。

五. Link-Time Optimization

Xcode -> Build Settings -> Apple Clang – Code Generation – Link-Time Optimization

LTO(Link-Time Optimization) 就是对整个程序代码进行的一种优化,是 LLVM 里在链接时进行跨模块间的优化。

iOS 包体积优化5 - 编译优化

支撑的链接选项

  • monolithic(adj. 整体的;)

    大型 LTO:这种形式对二进制进行大型的链接时优化,兼并一切的可履行代码到一个单元,而且履行更加急进的编译器优化。大型 LTO 的完成是把一切的输入兼并到一个模块,并没有考虑时刻和内存的问题,而且还阻止了增量编译的履行。

  • incremental(adj. 添加的,递增的;)

    这个形式能够对二进制履行部分的链接时优化,在编译单元之间进行内联,并行地在每个单元里履行更急进的编译器优化。这个能够允许更快的增量编译,以及运用更少的内存。

  • No 不做优化

苹果在WWDC2016对LTO的介绍如下:

What is Link-Time Optimization (LTO)?

Maximize runtime performance by optimizing at link-time Inline functions across source files Remove dead code Enable powerful whole program optimizations

将一些函数內联化

去除了一些无用代码

对程序有大局的优化作用

苹果官方称他们已经在他们的运用软件中很多运用LTO,而且比较惯例release形式在运转速度上提高了10%,此外它还会运用PGO(按装备优化来优化代码,而且还能减小代码体积

Apple uses LTO extensively internally Typically 10% faster than executables from regular Release builds Multiplies with Profile Guided Optimization (PGO) Reduces code size when optimizing for size

这里也带来了很明显的缺陷,特别是在有debug info的时分,代码编译耗时和更大的内存占用且二次编译的时分得全部从头编译。

LTO trades compile time for runtime performance Large memory requirements Optimizations are not done in parallel Incremental builds repeat all the work

LTO用编译时刻来交换运转时功能 优化不是并行进行的 增量构建重复一切的作业

总结来说: 敞开LTO首要是对链接过程的一个优化,而且有link cache,使二次编译的速度更快,另一方面它还很有或许减小code size。

该编译选项设置为
  • Debug 形式设置为No
  • Release 形式设置为incremental

六. Asset Catalog Compiler 之 Optimization

Build Settings -> Asset Catalog Compiler – Options -> Optimization

这个选项能够改动actool在构建Assets.car时选取的编码紧缩算法,削减包巨细。

改动actool(运用内置在Xcode中的compile asset catalog工具)在构建Assets.car时会按照一定策略选取编码算法,对其间的 png 图片从头编码, 然后削减包巨细。Assets.xcassets 紧缩格局对终究ipa包下assets.car文件巨细的影响较大。

iOS 包体积优化5 - 编译优化

能够把对应的信息生成json文件,比照差异

1、打一个ipa包,改为zip格局解压,进入Payload文件夹。翻开终端履行
cd /Users/Desktop/pluto/Payload/pluto.app
​
2、用find指令定位到Assets.car文件
find . -name 'Assets.car'
​
3、运用 assetutil 指令导出图片的信息存储到Assets.json文件中
sudo xcrun --sdk iphoneos assetutil --info ./Assets.car > /tmp/Assets.json
​
4、翻开生成的Assets.json文件
open /tmp/Assets.json

以项目中这个头像为例

iOS 包体积优化5 - 编译优化

能够看到紧缩算法为: “Compression” : “lzfse” (苹果开源的一种紧缩算法,还有其他的算法)。紧缩的算法不同,占用空间的巨细也不同。

{
 "AssetType" : "Image",
 "BitsPerComponent" : 8,
 "ColorModel" : "RGB",
 "Colorspace" : "srgb",
 "Compression" : "lzfse",
 "Encoding" : "ARGB",
 "Idiom" : "universal",
 "Name" : "AccountPhotoIcon",
 "NameIdentifier" : 46082,
 "Opaque" : false,
 "PixelHeight" : 96,
 "PixelWidth" : 96,
 "RenditionName" : "AccountPhotoIcon@2x.png",
 "Scale" : 2,
 "SHA1Digest" : "436897BD6B134BBAE8C0ADE4D57C13AC8266307AC9767672E7B50B35274A86F0",
 "SizeOnDisk" : 334,
 "State" : "Normal",
 "Template Mode" : "automatic",
 "Value" : "Off"
}
该编译选项设置为
  • Debug 形式设置为space
  • Release 形式设置为space

七. Make Strings Read-Only

Xcode -> Build Settings -> Apple Clang – Code Generation – Make Strings Read-Only

复用字符串字面量,望文生义就是削减生成不必要的量。

iOS 包体积优化5 - 编译优化

该编译选项设置为
  • Debug 形式设置为Yes
  • Release 形式设置为Yes

八. Dead Code Stripping

Xcode -> Build Settings -> Linking -> Make Strings Read-Only

C/C++/Swift 等静态言语编译器会在 link 的时分移除未运用的代码,可是关于 Objective-C 等动态言语是无效的。因为 Objective-C 是建立在运转时上面的,底层露出给编译器的都是 Runtime 源码编译成果,一切的部分都会被判别为有效代码。

iOS 包体积优化5 - 编译优化

该编译选项设置为
  • Debug 形式设置为Yes
  • Release 形式设置为Yes
  • Xcode 默许会敞开此选项

九. 优化调试符号

可履行文件中的符号是指程序中的一切的变量、类、函数、枚举、变量和地址映射联系,以及一些在调试的时分运用到的用于定位代码在源码中的位置的调试符号,符号和断点定位以及堆栈符号化有很重要的联系。

1. iOS 的调试符号

iOS 的调试符号是 DWARF 格局的,相关概念如下:

  • Mach-O: 可履行文件,源文件编译链接的成果。包含映射调试信息(目标文件)详细存储位置的 Debug Map。
  • DWARF:一种通用的调试文件格局,支撑源码级别的调试,调试信息存在于目标文件中,一般都比较大。Xcode 调试形式下一般都是运用 DWARF 来进行符号化的。
  • dSYM:独立的符号表文件,首要用来做发布产品的溃散符号化。dSYM 是一个紧缩包,里面包含了 DWARF 文件。

iOS 包体积优化5 - 编译优化

运用 Xcode 编译打包的时分会先经过可履行文件的 Debug Map 获取到一切目标文件的位置,然后运用 dsymutil将目标文件中的 DWARF 提取出来生成 dSYM 文件。

2. Strip Style

Xcode -> Build Settings -> Deployment -> Strip Style

表示的是咱们需求去除的符号的类型选项

iOS 包体积优化5 - 编译优化

  • All Symbols: 去除一切符号,一般是在主工程中敞开。
  • Non-Global Symbols: 去除一些非大局的 Symbol(保留大局符号,Debug Symbols 同样会被去除),链接时会被重定向的那些符号不会被去除,此选项是静态库/动态库的建议选项。
  • Debug Symbols: 去除调试符号,去除之后将无法断点调试。

挑选不同的Strip Style时,app构建末尾的Strip操作会被带上对应的参数。假如挑选debugging symbols的话,函数调用栈中,类名和方法名仍是能够看到的。

该编译选项设置为
  • Debug 形式设置为All Symbols
  • Release 形式设置为All Symbols

3. Deployment Postprocessing (这个设置为Yes 有会报错: error build: Command PhaseScriptExecution failed with a nonzero exit code)

Xcode -> Build Settings -> Deployment -> Deployment Postprocessing

Deployment Postprocessing是Strip装备的总开关,只要这个设置为YES之后,下面的Strip Linked Product、Strip Debug Symbols During Copy的设置才会收效。

PS:Deployment Postprocessing这个装备项假如运用xcode打包,xcode会默许把这个变量置为YES, 假如运用脚本打包,记住设置。

该编译选项设置为
  • Debug 形式设置为No
  • Release 形式设置为Yes

4. Strip Linked Product

Xcode -> Build Settings -> Deployment -> Strip Linked Product

并不是一切的符号都是必须的,比方 Debug Map,所以 Xcode 供给给咱们 Strip Linked Product 来去除不需求的符号信息(Strip Style 中挑选的选项相应的符号),去除了符号信息之后咱们就只能运用 dSYM 来进行符号化了,所以需求将 Debug Information Format 修正为 DWARF with dSYM file

iOS 包体积优化5 - 编译优化

4.1 疑惑没有 DWARF 调试信息之后 Xcode 是靠什么来生成 dSYM 的?

答案其实仍是 DWARF,因为 Xcode 编译实际的操作过程是:

生成带有 DWARF 调试信息的可履行文件 -> 提取可履行文件中的调试信息打包成 dSYM -> 去除符号化信息

去除符号是单独的过程,运用的是 strip 指令。

4.2 去除符号化信息之后咱们只能运用 dSYM 来进行符号化,那咱们运用 Xcode 来进行调试的时分会不会太麻烦了?

其实咱们彻底不用忧虑这个问题:Strip Linked Product 选项在 Deployment Postprocessing 设置为 Yes 的时分才收效,而在 Archive 的时分 Xcode 总是会把 Deployment Postprocessing 设置为 YES 。

所以咱们能够翻开 Strip Linked Product 而且把 Deployment Postprocessing 设置为 NO,而不用忧虑调试的时分会影响断点和符号化,一起打包的时分又会自动去除符号信息。

iOS 包体积优化5 - 编译优化

该编译选项设置为
  • Debug 形式设置为No
  • Release 形式设置为Yes
  • 这个选项是默许翻开的;

5. Strip Debug Symbols During Copy

Xcode -> Build Settings -> Deployment -> Strip Debug Symbols During Copy

Strip Linked Product 类似,可是这个是将那些复制进项目包的三方库、资源或许 Extension 的 Debug Symbol 去除掉,同样也是运用的 strip 指令。所以咱们只需求在 Release 形式下敞开,否则就不能对三方库进行断点调试和符号化了。

iOS 包体积优化5 - 编译优化

该编译选项设置为
  • Debug 形式设置为No
  • Release 形式设置为Yes

6. Strip Swift Symbols

Xcode -> Build Settings -> Deployment -> Strip Swift Symbols

能帮助咱们移除相应 Target 中的一切的 Swift 符号.

iOS 包体积优化5 - 编译优化

该编译选项设置为
  • Debug 形式设置为Yes
  • Release 形式设置为Yes
  • 该项是默许翻开的;

十. Symbols Hidden by Default

Xcode -> Build Setting -> Apple Clang – Code Generation -> Symbols Hidden by Default

用于设置符号默许可见性,XCode会把一切符号都定义为private extern,移除符号信息,包巨细会略有削减。动态库设置为NO,否则会有链接过错。

iOS 包体积优化5 - 编译优化

该编译选项设置为
  • Debug 形式设置为No
  • Release 形式设置为Yes
  • Framework工程 静态库/动态库,设置为NO,否则会有链接过错。

十一. Debug Information Level

Xcode -> Build Setting -> Apple Clang – Code Generation -> Debug Information Level

切换启用调试符号时宣布的调试信息的数量。这或许会影响生成的调试信息的巨细,这在大型项目的某些情况下或许很重要(例如运用LTO时)。

  • Compiler default

  • Line tables only

    这种类型的调试信息允许取得带有函数名、文件名和行号的函数调用栈,可是不包含其他数据(比方局部变量和函数参数)。所以当Debug Information Level设置为Line tables only的时分,断点依然会中止,可是无法在调试器中查看局部变量的值.

iOS 包体积优化5 - 编译优化

该编译选项设置为
  • Debug 形式设置为Compiler default
  • Release 形式设置为Compiler default
  • 这个选项默许就是 Compiler default;

十二. Generate Debug Symbols (不建议)

Xcode -> Build Setting -> Apple Clang – Code Generation -> Generate Debug Symbols

iOS 包体积优化5 - 编译优化

生成调试符号选项,当这个选项

  • 设置为YES时

    每个源文件在编译成.o文件时,编译参数多了-g和-gmodule,意思是generate complete debug info,所以发生的.o文件会大,然后终究生成的可履行文件也就会变大。

  • 设置为NO时

    在Xcode中不能断点调试。且最终不能生成DSYM文件,即使设置 Debug Information Format设置了,也不能生成。

    因为首先要有调试信息然后才能生成DSYM文件,而设置为NO,意味着不发生调试信息,所以也就没办法生成DSYM文件。

该编译选项设置为
  • 不建议改动,坚持设置为 yes

十三. Compress PNG Files

Xcode -> Build Setting -> Compress PNG Files – Packaging -> Compress PNG Files

iOS 包体积优化5 - 编译优化

当咱们在构建过程中,Xcode 会经过自己的紧缩算法从头对图片进行处理。经过调研知道 Apple 为了在优化 iPhone 设备读取 png 图片速度,将 png 转化成 CgBI 非标准的 png 格局:

  • extra critical chunk (CgBI)

    额定的关键数据块 CgBI

  • byteswapped (RGBA -> BGRA) pixel data, presumably for high-speed direct blitting to the framebuffer

    字节转化 加速数据交换

  • zlib header, footer, and CRC removed from the IDAT chunk

    移除一些辅助性数据块

  • premultiplied alpha (color’ = color * alpha / 255)

    预乘透明度

在苹果此项的优化下,一般的紧缩(有损,无损)处理并不能达到很好的瘦身作用,关于大多数运用来说都是包巨细的 负优化

该编译选项设置为
  • 不建议改动,坚持设置为 Yes

十四. Remove Text Metadata From PNG Fils

Xcode -> Build Setting -> Compress PNG Files – Packaging -> Remove Text Metadata From PNG Fils

能帮助咱们移除 PNG 资源的文本字符,比方图像称号、作者、版权、创作时刻、注释等信息。

该编译选项设置为
  • 坚持设置为 Yes

十五. 去掉异常支撑 (不建议)

Xcode -> Build Setting -> Apple Clang – Language – C++ -> Enable C++ Exceptions

Xcode -> Build Setting -> Apple Clang – Language – C++ -> Enable Objective-C Exceptions

Xcode -> Build Setting -> Apple Clang – Code Generation -> Other C Flags

1. Enable C++ Excptions 和 Enable Objective-C Exceptions 设置为 No

是指项目支撑对过错的异常处理。比方try catch、throw之类的;所以假如项目中运用的有类似的异常处理的,这个封闭了之后会报错(Cannot use ‘@try’ with Objective-C exceptions disabled)。

包含宏定义中运用的有try{}、@finally{}之类的,比方@strongify等,假如封闭了最终打包的时分也会报错。

2. Other C Flags添加 -fno-exceptions

-fno-exceptions的意思是禁用异常机制,当项目中有try thorw的时分,就不要设置这个。