iOS中的静态库和动态库
静态库
- 静态库完全复制进可执行的二进制里面
- 后缀是
.a
或者.framework
动态库
- 动态库是在程序冷启动时候被链接到手机内存或者 App 内存里面
- 后缀是
.tbd
或者.framework
关于说明很多,可以看一下网上的文章。我现在说的是基于上面理论的证明。刚开始觉得网上说的可能不太准确,后来得到验证是正确的。
为了研究我们创建的.a
和.framework
到底是静态库和动态库,我们分别创建对应的简单的库。
我们分别可以通过上面图中红色区域创建.framework
和.a
。我们知道区分是静态库还是动态库最终是我们选择的 Mach-O
的类型到底是Dynamic Library
还是Static Library
。
Mach-O 类型
这里简单说一下Mach-O
类型,为什么说简单说一下。因为深入我也不了解了,深入可以谷歌资料。
-
Executable:应用的主要二进制
-
Dylib Library:动态链接库(又称DSO或DLL)
-
Static Library:静态链接库
-
Bundle:不能被链接的Dylib,只能在运行时使用dlopen( )加载,可当做macOS的插件
-
Relocatable Object File:可重定向文件类型
静态库和动态库的对比
为了让打出来的库更加的真实,我们使用网上出名的库作为测试。我们选取的是FLEX作为测试的目标,因为这个库所包含的文件多,数据真实性更加的可靠。我们编译的环境是基于iPhone 6s Plus
进行编译出来的,真正的大小会包含其他框架会比我测试大得多。
二进制大小
二进制大小(iPhone 6s Plus) | .a | .framework |
---|---|---|
静态库 | 6.8MB | 4.6MB |
动态库 | 1.6MB | 1.6MB |
为了验证我们所谓静态库和动态库是否是真正的静态库和动态库,我们使用File
命令和Mach-O查看软件分别对比一下。
File 命令
输出显示 | .a | .framework |
---|---|---|
静态库 | ✅(current ar archive) | ✅(current ar archive) |
动态库 | ✅(Mach-O 64-bit dynamically linked shared library x86_64) | ✅(Mach-O 64-bit dynamically linked shared library x86_64) |
Mach-O 查看
输出显示 | .a | .framework |
---|---|---|
静态库 | ✅(Static Library) | ✅(Static Library) |
动态库 | ✅(Shared Library) | ✅(Shared Library) |
我们对比结果发现,不管是.a
还是.framework
都可以作为动态链接库来使用,这和我们在网上看到文章说.a
是静态库是不严谨的。
作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:519832104 不管你是小白还是大牛欢迎入驻,分享经验,讨论技术,大家一起交流学习成长!
另附上一份各好友收集的大厂面试题,需要iOS开发学习资料、面试真题,可以添加iOS开发进阶交流群,进群可自行下载!
我们按照静态库是完整被拷贝到工程二进制里面,动态库是在启动时候动态链接的描述,我们分别对比一下运行包里面的表现。我们同样是按照iPhone 11 Pro Max
做为对比的,这样数据比较真实。
静态库和动态库在包里面的表现
Static Library
的.a
和.framework
代码所在的位置
代码所在位置(iPhone 6s Plus) | .a | .framework |
---|---|---|
静态库 | App 二进制中 | App 二进制和 Frameworks 文件夹中 |
动态库 | 无(二进制没有 Frameworks文件夹 没有) | Frameworks 文件夹中 |
App 二进制大小对比
App二进制大小(iPhone 6s Plus) | .a | .framework |
---|---|---|
静态库 | 93KB | 93KB |
动态库 | 92KB | 92KB |
启动速度对比
冷启动
冷启动速度(iPhone 6s Plus) | .a | .framework |
---|---|---|
静态库 | 151.44毫秒 | 53.35毫秒 |
动态库 | 0 毫秒(运行报错) | 254.58毫秒 |
热启动
热启动速度 | .a | .framework |
---|---|---|
静态库 | 160.99 毫秒 | 154.65 毫秒 |
动态库 | 0 毫秒(运行报错) | 199.04 毫秒 |
这里描述的冷启动是指代程序第一次安装之后运行可能会加载系统动态库而造成启动时间边长,热启动是第二次启动不需要加载系统动态库。
通过上面的代码位置,二进制大小,冷启动速度和热启动速度大概可以得出一个结论。虽然严格来说.a
是支持打包出来动态库的,但是代码不会复制进二进制,也不会存放在Frameworks
里面。造成我们打出来的动态库.a
在程序里面无法运行,也就换句话说.a
只支持静态库特征了。
虽然.a
的静态库的热启动比冷启动还要慢,但是这几毫秒的误差可以抛弃。可能基于我们手机已经打开了其他应用造成冷启动没有再次加载系统动态库而和热启动速度差不多。
但是对比.framework
可以得知静态库
的启动速度确实比动态库
的加载速度快很多。而静态库的.framework
会存在两份,所以安装包会变大,因为静态库的.framework
直接复制进二进制里面,所以二进制会变大。
通过上面我们得出下面的结论
.a
只支持静态库(打包出动态库也是支持的 只不过目前 iOS 不支持加载).framework
静态库会存在于二进制和Frameworks
两份。- 热启动会比冷启动快的多
- 静态库比动态库加载快得多
- 静态库
.framework
比.a
的二进制文件小的多 - 静态库和动态库都推荐用
.framework
不推荐用.a
XCFrameworks
XCFrameworks 和之前 Framework 对比
说完了我们常用的动态库和静态库,我们说一下今年出的新特征XCFramework
。根据之前一篇文章了解,XCFramework
是为了取代之前的.framework
的。
我很想深入的探究一下XCframeworks
来谈一下用这个的好处,但是苹果对于这个的资料少之又少。我们通过真实的对比一下XCFrameworks
的好处。
我们通过对比包含模拟器
和真机
的.framework
和对比.xcframework
对比一下数据。
对于生成多架构的.framework
我们需要用到lipo
命令进行合成,但是对于生成.xcframework
可以用到我的xcbuild
的脚本生成。
(X86_64 + ARM64) | .framework | .xcframework |
---|---|---|
文件大小 | 10.6MB | 10.8MB |
二进制大小 | 10.4MB | 8.3MB+2.2MB |
App 大小(iPhone 6S Plus Debug) | 4.8MB | 2.8MB |
冷启动 | 169.8 毫秒 | 155.52毫秒 |
热启动 | 159.56 毫秒 | 153.62 毫秒 |
通过对比.xcframework
比.framework
不但 App 大小少了很多,而且还对于启动速度提升了不少。
我们之前说的可以将.a
和头文件打包到.xcframework
中,我们下面尝试一下。
融合.a 和头文件
我们事先准备好模拟器和真机架构的.a
和头文件,使用下面的命令创建一个XCFrameworks
.
xcodebuild -create-xcframework -library <path> [-headers <path>] [-library <path> [-headers <path>]...] -output <path>
//Example
xcodebuild -create-xcframework -library /Users/zhangxing/Library/Developer/Xcode/DerivedData/MyLibrary-frqlmvciureprnbnytogqzjagose/Build/Products/Debug-iphonesimulator/libMyLibrary.a -headers /Users/zhangxing/Library/Developer/Xcode/DerivedData/MyLibrary-frqlmvciureprnbnytogqzjagose/Build/Products/Debug-iphonesimulator/include/MyLibrary -library /Users/zhangxing/Library/Developer/Xcode/DerivedData/MyLibrary-frqlmvciureprnbnytogqzjagose/Build/Products/Debug-iphoneos/libMyLibrary.a -headers /Users/zhangxing/Library/Developer/Xcode/DerivedData/MyLibrary-frqlmvciureprnbnytogqzjagose/Build/Products/Debug-iphoneos/include/MyLibrary -output /Users/zhangxing/Library/Developer/Xcode/DerivedData/MyLibrary-frqlmvciureprnbnytogqzjagose/Build/Products/MyLibrary.xcframework
包含第多个不同的Framework(不支持)
假设我们的MyFramework
依赖一个我们自己的另外的库或者其他第三方的库
xcodebuild -create-xcframework -framework <path> [-framework <path>...] -output <path>
//Example
xcodebuild -create-xcframework -framework /Users/zhangxing/Downloads/data/MyFrameworkB.xcframework/ios-arm64/MyFrameworkB.framework -framework /Users/zhangxing/Downloads/data/MyFrameworkB.xcframework/ios-x86_64-simulator/MyFrameworkB.framework -framework /Users/zhangxing/Downloads/data/ios-arm64/MyFramework.framework -framework /Users/zhangxing/Downloads/data/ios-x86_64-simulator/MyFramework.framework -output /Users/zhangxing/Downloads/data/MyFrameworkC.xcframework
结果是一个 XCFrameworks
只能包含一个框架,但是可以包含多个架构。和之前写的文章写的理解有误,这里纠正一下。
怎么将之前已经Fat Framework
变成最新的XCFramework
如果有源码可以直接使用xcbuild
命令生成,如果没有或者是其他第三方的可以使用下面的命令。
lipo 静态库源文件路径 -thin CPU架构名称 -output 拆分后文件存放路径
用分离出来的架构重新合成XCFrmaework
。