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
。