静态库 & 动态库

-
静态库(Static Library):通常会加快冷启动速度(相比于多个动态库)。
-
动态库(Dynamic Library):如果是自定义的动态库(Embedded Framework),数量多了会严重拖慢冷启动时间(Apple 建议不超过 6 个)。
1. 静态库 vs 动态库:核心区别
可以用 “盖房子” 来比喻:
1.1 静态库 (.a, .framework 的 Mach-O Type 为 Static)
-
链接方式(Copy):
-
在编译链接阶段,链接器(Linker)会将静态库中的代码(指令)完整拷贝到你的 App 可执行文件(Mach-O)中。
-
比喻:像砌墙。你买了现成的砖块(静态库),直接砌进你的墙体里。房子盖好后(编译完),砖块就成了墙的一部分,以后不需要再单独搬运砖块了。
-
-
内存表现:
- 每个 App 都有自己独立的一份代码副本,不共享。
1.2 动态库 (.dylib, .framework 的 Mach-O Type 为 Dynamic)
-
链接方式(Reference):
-
链接器不拷贝代码,只在主程序里留一个引用(指针/签名)。等到 App 运行时(Runtime),系统(dyld)才去加载这个库。
-
比喻:像借工具。你盖房子时只写了个条子“我要用电钻”(引用)。等房子真正要装修(启动运行)时,你才去工具房把电钻拿过来用。
-
-
内存表现:
-
系统库(UIKit, Foundation):系统共享一份,所有 App 共用(通过 Shared Cache 技术),速度极快。
-
自定义库(Embedded Framework):虽然叫动态库,但在 iOS 沙盒机制下,实际上每个 App 也是各自拷贝一份在自己的 Bundle 里,并不能跨 App 共享内存(除非是系统级的)。
-
| 特性 | 静态库 (Static) | 动态库 (Dynamic) |
| 文件格式 | .a, .framework | .dylib, .framework |
| 链接行为 | 编译时拷贝代码 | 运行时加载引用 |
| App 包体积 | 较大(代码在主程中) | 较小(主程小,但加上库文件总体积差不多) |
| 加载时机 | 随主程序启动加载 | dyld 单独加载、链接 |
2. 为什么静态库“不会”增加冷启动时间?
你担心的点可能是:“静态库合并进去了,主 Mach-O 变大了,读取不需要时间吗?”
2.1 虚拟内存的“按需加载”机制
还记得我们上一篇讲的 缺页中断(Page Fault) 吗?
-
操作系统不会在启动时把整个 100MB 的 Mach-O 文件一次性读入 RAM。
-
它只会读取启动那一刻需要执行的代码。
-
结论:即使你的静态库让 Mach-O 变成 200MB,只要启动时没调用这个静态库里的代码,它就永远躺在磁盘上,完全不影响启动速度。
2.2 减少了 dyld 的工作量(关键优势)
冷启动的 Pre-main 阶段,dyld(动态链接器)非常忙。如果我们使用 10 个自定义动态库:
-
Load dylibs:dyld 要查找、验证签名、映射 10 个文件到内存。
-
Rebase/Bind:每个动态库都有自己的符号表,dyld 需要修正 10 个库里的指针偏移(Rebase)和外部符号绑定(Bind)。这部分是 O(n) 复杂度,消耗大量 CPU。
-
ObjC Setup:需要注册 10 个库里的类、Category。
如果换成静态库:
这 10 个库的代码直接合并到了主程序里。dyld 眼里只有一个文件(主程序)。
-
不需要重复验证签名。
-
Rebase/Bind 的工作量大幅减少(内部调用变成了直接跳转,不需要 dyld 修正)。
-
省去了昂贵的 Context Switch 和 I/O 切换。
3. 静态库唯一的“坑”与解法
虽然静态库整体更快,但在一种情况下会导致变慢:代码布局分散。
-
问题:
静态库的代码被简单追加在主程序末尾。如果启动时你需要频繁调用静态库里的 funcA,而 funcA 被链接到了文件的第 1000 页。-
CPU 就要跳到第 1000 页去读(触发 Page Fault)。
-
如果静态库很大,且启动代码分散在各个角落,就会导致大量的 Page Fault。
-
-
解法(完美闭环):
这正是 二进制重排(Binary Reordering) 发挥作用的地方!-
通过二进制重排,我们可以把静态库里那些“启动时真正用到的函数”,抠出来放到 Mach-O 的最前面(第 1 页)。
-
结合技:静态库合并 + 二进制重排 = iOS 启动速度的极致。
-
4. 面试满分总结话术
如果面试官问:“静态库和动态库有什么区别?静态库包体积大,会不会拖慢启动?”
建议回答:
-
区别定义:
-
静态库在编译链接期将代码完整拷贝到可执行文件中;
-
动态库在编译期只保留引用,运行时由 dyld 加载。
-
在 iOS 中,除了系统库外,自定义动态库无法在 App 间共享,因此相比静态库优势不明显。
-
-
启动性能分析(破除误区):
-
静态库反而更快。因为 iOS 采用了虚拟内存(分页加载)机制,文件变大并不意味着启动要全部读取,是按需读取的。
-
相反,使用静态库减少了 dyld 在 Pre-main 阶段加载多个 Image、进行 Rebase(重定位)和 Bind(符号绑定)的耗时。Apple 官方也建议尽量减少自定义动态库的数量(<6个)。
-
-
进阶补充:
- “虽然静态库会导致主二进制文件变大,可能导致启动代码分散。但我们可以结合二进制重排技术,将静态库中在启动阶段用到的函数排列到文件头部,从而同时享受到‘减少 dyld 耗时’和‘减少 Page Fault’的双重红利。”
这样回答,既解释了基础,又反驳了误区,还把话题引回了你擅长的二进制重排,体现了技术深度。