动态库与静态库
1. 符号种类与Common Symbol 2. 3.(.a)与.framework静态库详解
- shell初探
- dead_strip与静态库
- .o⽂件的合并与.o⽂件链接静态库的区别
- 静态库的合并
动态库
-
dead strip 2. 动态库.dylib.framework编译链接详解
-
install_name与@rpath
-
tdb格式动态库 5. 符号与库
-
-noall_load
-
-all_load
-
-ObjC
-
-force_load
-dead_strip
常用库文件格式
- .a
- .dylib
- .framework
- .xcframework
XCFramework
-
XCFramework:是苹果官⽅推荐的、⽀持的,可以更⽅便的表示⼀个多个平台和架构的分发⼆进制库的格式。
-
需要Xcode11以上⽀持。
-
是为更好的⽀持Mac Catalyst和ARM芯⽚的macOS。
-
专⻔在2019年提出的framework的另⼀种先进格式。
-
iOS/iPad:arm64
-
iOS/iPad Simulator:x86_64 arm64
-
Mac Catalyst: x86_64 arm64
-
Mac: x86_64 arm64
和传统的framework相⽐:
1. 可以⽤单个.xcframework⽂件提供多个平台的分发⼆进制⽂件;
2. 与Fat Header相⽐,可以按照平台划分,可以包含相同架构的不同平台的⽂件;
3. 在使⽤时,不需要再通过脚本去剥离不需要的架构体系。
什么是库(Library)?
- 库(Library)说⽩了就是⼀段编译好的⼆进制代码,加上头⽂件就可以供别⼈使⽤。
什么时候会⽤到库(Library)?
1. 某些代码需要给别⼈使⽤,但是我们不希望别⼈看到源码,就需要以 库的形式进⾏封装,只暴露出头⽂件。
2. 对于某些不会进⾏⼤的改动的代码,我们想减少编译的时间,就可以把它打包成库,因为库是已经编译好的⼆进制了,编译的时候只需要 Link ⼀下,不会浪费编译时间。
什么是链接(Link)?
库在使⽤的时候需要链接(Link),链接 的⽅式有两种:
1. 静态
2. 动态
什么是静态库?
- 静态库即静态链接库:可以简单的看成⼀组⽬标⽂件的集合。即很多⽬
- 标⽂件经过压缩打包后形成的⽂件。Windows 下的 .lib,Linux 和 Mac下的 .a。Mac独有的.framework。
缺点:
- 浪费内存和磁盘空间,模块更新困难
什么是动态库?
- 与静态库相反,动态库在编译时并不会被拷⻉到⽬标程序中,⽬标程序中只会存储指向动态库的引⽤。等到程序运⾏时,动态库才会被真正加载进来。格式有:.framework、.dylib、.tdb。
缺点:
- 会导致⼀些性能损失。但是可以优化,⽐如延迟绑定(Lazy Binding)技术
什么是tdb格式?
- tbd全称是text-based stub libraries,本质上就是⼀个YAML描述的⽂本⽂件。
- 他的作⽤是⽤于记录动态库的⼀些信息,包括导出的符号、动态库的架构信息、动 态库的依赖信息
- ⽤于避免在真机开发过程中直接使⽤传统的dylib。
- 对于真机来说,由于动态库都是在设备上,在Xcode上使⽤基于tbd格式的伪 framework可以⼤⼤减少Xcode的⼤⼩。
Framework
-
Mac OS/iOS 平台还可以使⽤ Framework。Framework 实际上是⼀种打包⽅式,将库的⼆进制⽂件,头⽂件和有关的资源⽂件打包到⼀起,⽅便管理和分发。
-
Framework 和系统的UIKit.Framework 还是有很⼤区别。系统的Framework不需要拷⻉到⽬标程序中,我们⾃⼰做出来的 Framework 哪怕是动态的,最后也还是要拷⻉到 App 中(App 和Extension 的 Bundle 是共享的),因此苹果⼜把这种 Framework 称为Embedded Framework。
Embedded Framework
- 开发中使⽤的动态库会被放⼊到ipa下的framework⽬录下,基于沙盒运⾏。
- 不同的App使⽤相同的动态库,并不会只在系统中存在⼀份。⽽是会在多个App中各⾃打包、签名、加载⼀份。
Mach-o File Format
- ⼀个Mach-o⽂件有两部分组成:header 和data。
- header:代表了⽂件的映射,描述了⽂件的内容以及⽂件所有内容所在的位置。
- data:紧跟header之后,由多个⼆进制组成,one by one。
Load Commands
- 进制⽂件加载进内存要执⾏的⼀些指令。
- 这⾥的指令主要在负责我们APP 对应进程的创建和基本设置(分配虚拟内存,创建主线程,处理代码签名/加密的⼯作),然后对动态链接库(.dylib系统库和我们⾃⼰创建的动态库)进⾏库加载和符号解析的⼯作。
Mach-o File Format
- ⼀个Mach-o⽂件有两部分组成:header 和data。
- header:包含三种类型。Mach header,segment,sections
- header内的section描述了对应的⼆进制信息。
- 注意⚠:Mach header属于header的⼀部分,它包含了整个⽂件的信息和segment信息。
App framework存放位置
Embedded Framework
- 开发中使⽤的Embedded Framework会被放⼊到ipa下的Frameworks⽬录下,基于沙盒运⾏。
- 不同的App使⽤相同的动态库,并不会只在系统中存在⼀份。⽽是会在多个App中各⾃打包、签名、加载⼀份。
workspace
A. 可重⽤性。多个模块可以在多个项⽬中使⽤。节约开发和维护时间。
B. 节省测试时间。单独模块意味着每个模块中都可以添加测试功能。
C. 更好的理解模块化思想。
macOS是Big endian or little endian?? iOS呢??
- 168496141 = 0x0A0B0C0D
magic number
Mach header
- magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
- 0xfeedfacf 16777223 3 0x00 1 4 1400 0x00002000
Mach-o File Format
-
⼀个Mach-o⽂件有两部分组成:header 和data。
-
Segments(segment commands): 指定操作系统应该将Segments加载到内存中的什么位置,以及为该Segments分配的字节数。还指定⽂件中的哪些字节属于该Segments,以及⽂件包含多少 sections。Mac上始终是4096字节或4 KB的倍数,其中4096字节是最⼩⼤⼩。iOS上是8 KB。Segments名称的约定是使⽤全⼤写字⺟,后跟双下划线(例如__TEXT)。
-
Section: 所有sections都在每个segment之后⼀个接⼀个地描述。sections⾥⾯定义其名称,在内存中的地址,⼤⼩,⽂件中section数据的偏移量和segment名称。Section的名称约定是使⽤全⼩写字⺟,再加上双下划线(例如__text)。
-
⼀个Mach-o⽂件由两部分组成:header 和data。
-
__TEXT段:只读区域
-
包含可执⾏代码和常量数据。
-
__DATA段:读/写
-
包含初始化和未初始化数据和⼀些动态链接专属数据。
-
'int main() { }' compiled for x86_64-apple-macosx with clang
-
0x55, // offset 0 -- pushq %rbp
-
0x48, 0x89, 0xe5, // offset 1 -- movq %rsp, %rbp
-
0x31, 0xc0, // offset 4 -- xorl %eax, %eax
-
0x5d, // offset 6 -- popq %rbp
-
0xc3 // offset 7 -- retq
-
那么要验证链接动态库与静态对ipa包体积的影响, 先要明⽩ipa格式到底是什么?
-
源码⽂件越少,链接的外部函数越少,⽣成的静态库要⽐动态库体积⼩
-
其次,静态库中某个⽬标⽂件中的代码没有在项⽬中引⽤,那么就不会链接到项⽬的Mach-o⽂件内。
-
也就是,静态库默认仅将⽤到的⽂件链接进去。
-
并且以类⽂件为最⼩链接单位。
-
还有⼀个问题,⽣成库⽂件的时候链接三⽅库会是什么样的情况?
-
静态库链接动态库
-
静态库链接静态库
-
动态库链接静态库
-
动态库链接动态库
@rpath
- Runpath search Paths
- dyld搜索路径
- 运⾏时
@rpath指示dyld按顺序搜索路径列表,以找到动态库。 @rpath保存⼀个或多个路径的变量@executable_path:表示可执⾏程序所在的⽬录,解析为可执⾏⽂件的绝对路径。@loader_path:表示被加载的Mach-O所在的⽬录,每次加载时,都可能 被设置为不同的路径,由上层指定。
静态链接
Swift静态库的合并
- 在 Xcode 9 之后,Swift 开始⽀持静态库。
- Swift 没有头⽂件的概念,那么我们外界要使⽤Swift中⽤public修饰的类和函数怎么办?
- Swift库中引⼊了⼀个全新的⽂件.swiftmodule。
- .swiftmodule 包含序列化过的 AST(抽象语法树,Abstract Syntax
- Tree),也包含 SIL(Swift 中间语⾔,Swift Intermediate Language)。
Mach-o File Format
'int main() { }' compiled for x86_64-apple-macosx with clang
- 0x55, // offset 0 -- pushq %rbp
- 0x48, 0x89, 0xe5, // offset 1 -- movq %rsp, %rbp
- 0x31, 0xc0, // offset 4 -- xorl %eax, %eax
- 0x5d, // offset 6 -- popq %rbp
- 0xc3 // offset 7 -- retq
- 2013年9⽉之前,蜂窝⽹络下载App⼤⼩的限制为50 MB;
- 2013年9⽉,iOS 7正式版后,蜂窝⽹络下载App的限制,从 50 MB 提升⾄100 MB
- 2017年9⽉,iOS11正式版本后,限制从 100 MB 提升⾄ 150 MB,并在
- 2019年5⽉下旬,将 150 MB放宽到200MB;
- 2019年9⽉,iOS13正式版本后,放开了蜂窝⽹络下App下载⼤⼩的限制
- 根据Apple的审核要求,上传App Store的ipa的可执⾏⽂件有⼤⼩限制,这⾥的⼤⼩不是指⼆进制(Mach-O)⽂件⼤⼩,⽽是指其中的__TEXT部分的⼤⼩。
- IOS 7版本之前, ⼆进制⽂件中所有__TEXT部分总和不得超过80MB
- iOS 7.X ⾄ iOS 8.X,⼆进制⽂件中,每个架构中的__TEXT部分不得超过60MB
- iOS 9.0之后,⼆进制⽂件中所有__TEXT部分的总和不超过500 MB
Bitcode
⾸先,就是⼤家开发中⾮常关注的问题
- 静态库
- VS 动态库
- 分发体积
- 链接到App中后到体积
- 对于iOS9,Load commands segment vmsize默认是⼀个内存⻚,也就是16k。
- 对于iOS10,Load commands segment vmsize默认是32k。
- vmsize:此segment占⽤的虚拟内存的字节数。
- filesize:指示此segment在磁盘上占⽤的字节数。
App链接静态库
App链接动态库
- -noall_load
- -all_load
- -ObjC
- -force_load
- ld(链接器)-map <map_file_path>: 显示所有的符号信息和链接的信息
- ld(链接器)参数在通过Clang传递时,可以使⽤-Xlinker or -Wl传递
- Code size 优化
- Compiler层
Bitcode
• 精简代码
• 调⽤过程优化
• 内联优化
Link层
- Link Time Optimization(LTO)