在iOS開(kāi)發(fā)中,將特定功能代碼封裝在一個(gè)庫(kù)中避除,對(duì)外提供接口調(diào)用怎披,這樣方便維護(hù)和集成,如網(wǎng)絡(luò)庫(kù)瓶摆。庫(kù)有靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)凉逛,我們?cè)诩蓵r(shí)該選擇哪種?制作自己的庫(kù)時(shí)群井,該如何指定状飞?
一、問(wèn)題引出
在使用CocoaPods管理三方庫(kù)時(shí)蝌借,Podfile
文件中關(guān)于use_frameworks!
的使用有以下特點(diǎn):
說(shuō)明:
s.static_framework = true\false
用于在庫(kù)的podspec文件中聲明是否要生成靜態(tài)庫(kù)昔瞧。
Podfile選項(xiàng) \ Podspec | s.static_framework = true | s.static_framework = false |
---|---|---|
use_frameworks! | 靜態(tài)庫(kù) | 動(dòng)態(tài)庫(kù) |
不使用use_frameworks! | 靜態(tài)庫(kù)指蚁,swift項(xiàng)目導(dǎo)入庫(kù)時(shí)報(bào)錯(cuò)菩佑,OC不會(huì) | 靜態(tài)庫(kù),swift項(xiàng)目導(dǎo)入庫(kù)報(bào)錯(cuò)凝化,OC不會(huì) |
use_frameworks! :linkage => :static | 靜態(tài)庫(kù) | 靜態(tài)庫(kù) |
use_frameworks! :linkage => :dynamic | 靜態(tài)庫(kù) | 動(dòng)態(tài)庫(kù) |
結(jié)論:
1.swift項(xiàng)目需要使用
use_frameworks!
選項(xiàng)稍坯,后面可以接:linkage => :static等。
2.庫(kù)通過(guò)s.static_framework = true
指定了生成靜態(tài)庫(kù)搓劫,那么集成時(shí)就會(huì)是靜態(tài)庫(kù)瞧哟。
3.庫(kù)沒(méi)有通過(guò)s.static_framework = true
指定靜態(tài)庫(kù),那么集成時(shí)可以通過(guò)上述表格中方式控制枪向。
現(xiàn)在我們知道了如何通過(guò)pod控制集成動(dòng)勤揩、靜態(tài)庫(kù),那么動(dòng)態(tài)庫(kù)秘蛔、靜態(tài)庫(kù)究竟有啥區(qū)別陨亡?
二、動(dòng)深员、靜態(tài)庫(kù)簡(jiǎn)單對(duì)比
對(duì)比項(xiàng) | 靜態(tài)庫(kù) | 動(dòng)態(tài)庫(kù) |
---|---|---|
文件格式 |
.a 负蠕、framework 、xcframework
|
.tbd 倦畅、.dylib 遮糖、framework 、xcframework
|
編譯鏈接 | 鏈接時(shí)合并到可執(zhí)行文件中叠赐;推送擴(kuò)展等插件依賴了該庫(kù)也會(huì)拷貝一份 | 編譯鏈接時(shí)不合并欲账,會(huì)獨(dú)立生成一個(gè)動(dòng)態(tài)庫(kù)類型的Mach-O文件屡江,放在xxx.app/Frameworks/xxx.framework中;其它擴(kuò)展赛不、插件依賴了不會(huì)再生成一份 |
對(duì)啟動(dòng)影響 | 內(nèi)容跟隨主二進(jìn)制加載到內(nèi)存盼理,對(duì)啟動(dòng)影響較小 | 啟動(dòng)時(shí)dyld會(huì)加載其Mach-O并進(jìn)行符號(hào)解析,相比靜態(tài)庫(kù)更耗時(shí) |
包體積 | 如果主工程依賴該庫(kù)而擴(kuò)展插件未依賴俄删,那么包體積會(huì)相比做成動(dòng)態(tài)庫(kù)小一點(diǎn) | 相比靜態(tài)庫(kù)要大一丁點(diǎn) |
三宏怔、了解靜態(tài)庫(kù)
Static libraries are collections or archives of object files.
即靜態(tài)庫(kù)是.o文件的集合或歸檔。
1. iOS工程中的靜態(tài)庫(kù)
靜態(tài)庫(kù)主要文件形式是.a文件,以及靜態(tài)庫(kù)類型的framwork畴椰。使用CocoaPos集成的庫(kù)臊诊,如果在Podfile中寫(xiě)了:
install! 'cocoapods',
# 禁用輸入輸出路徑,不在生成的 Xcode 項(xiàng)目中包含特定的輸入輸出路徑斜脂,從而避免一些可能的兼容性問(wèn)題抓艳。
disable_input_output_paths: true,
generate_multiple_pod_projects: true # 讓每個(gè)pod依賴庫(kù)成為一個(gè)單獨(dú)的項(xiàng)目引入,這樣大大提升了編譯速度
那么每個(gè)pod install
后每個(gè)庫(kù)會(huì)對(duì)應(yīng)一個(gè)工程帚戳,可以通過(guò)在對(duì)應(yīng)工程的build setting
中查看Mach-O type
確定靜態(tài)庫(kù)還是動(dòng)態(tài)庫(kù)玷或。
關(guān)于Mach-O基本了解可以查看:iOS中Mach-O概覽
2. framework
靜態(tài)庫(kù)和.a
的區(qū)別
》.a是一個(gè)純二進(jìn)制文件,.framework中有二進(jìn)制文件片任、頭文件偏友、資源文件、模塊文件Modules对供。
》常規(guī)開(kāi)發(fā)時(shí)位他,.a文件不能直接使用,至少要有.h文件配合产场,.framework文件可以直接使用鹅髓。
》.a + .h + sourceFile + Modules = .framework。
純swift生成靜態(tài)庫(kù)時(shí)京景,如果生成.a
靜態(tài)庫(kù)無(wú)法直接使用窿冯,建議生成framework
靜態(tài)庫(kù)。(swift語(yǔ)言生成.a文件后确徙,我們拖入項(xiàng)目中使用會(huì)發(fā)現(xiàn)import
模塊會(huì)報(bào)錯(cuò):No such module 'XXX'
)
3. 制作靜態(tài)庫(kù)
3.1手動(dòng)制作一個(gè)靜態(tài)庫(kù)
你自己根據(jù)網(wǎng)上資料的方法操作生成醒串,但如果給其他庫(kù)中定義的類增加OC分類時(shí),需要注意是否需要增加-Objc編譯標(biāo)識(shí)米愿。
3.2 通過(guò)cocoapods
方法一:
在庫(kù)的podspec
文件中聲明生成靜態(tài)庫(kù)厦凤,這樣在pod install
之后,xcode最終會(huì)編譯為靜態(tài)庫(kù)育苟。
# 在庫(kù)的podspec文件中寫(xiě)如下設(shè)置
s.static_framework = true
方法二:
在Podfile中集成庫(kù)時(shí)聲明:
use_frameworks! :linkage => :static
或者另一個(gè)方法在pre_install中進(jìn)行hook設(shè)置static_framework
4. 靜態(tài)庫(kù)framework里面的構(gòu)成
我們打開(kāi)手動(dòng)制作的靜態(tài)庫(kù)framework较鼓,里面的文件有:
文件夾 | 內(nèi)容 |
---|---|
framework靜態(tài)庫(kù) |
framework靜態(tài)庫(kù).png
|
Modules | Modules文件詳情.png
|
5. Modules
蘋(píng)果推出Modules
主要是為了支持模塊化編程香椎,提供更清晰的代碼組織和更好的命名空間管理。Modules文件夾里放著.modulemap
和.swiftmodule
文件禽篱。.modulemap
是用于C\OC畜伐,.swiftmodule
用于swift。
有了Modules后躺率,對(duì)于OC庫(kù)玛界、Swift庫(kù)、OC+Swift混編的庫(kù)悼吱,外部使用時(shí)都可以導(dǎo)入模塊慎框,不需要導(dǎo)入頭文件,使得開(kāi)發(fā)更加方便后添。
@import WebKit.WebKitLegacy; //in Objective-C
import WebKit.WebKitLegacy //in Swift
細(xì)節(jié)可以閱讀:開(kāi)發(fā)進(jìn)階-Module與Swift庫(kù)
6. 制作靜態(tài)庫(kù)注意點(diǎn)
6.1 靜態(tài)庫(kù)符號(hào)沖突
符號(hào)沖突指的是在OC/C的靜態(tài)庫(kù)中笨枯,全局變量、靜態(tài)變量或函數(shù)名如果與應(yīng)用程序或其他靜態(tài)庫(kù)中的全局符號(hào)相同遇西,導(dǎo)致沖突發(fā)生符號(hào)重復(fù)定義錯(cuò)誤. (親測(cè)馅精,如果多個(gè)靜態(tài)庫(kù)中有相同名稱的OC分類,不會(huì)導(dǎo)致符號(hào)沖突粱檀,對(duì)于相同名稱分類中相同方法的調(diào)用洲敢,最終調(diào)用的方法實(shí)現(xiàn)是較晚編譯的庫(kù)中方法。)
使用Swift庫(kù)一般不會(huì)產(chǎn)生符號(hào)沖突梧税,Swift 引入了模塊化編程的概念沦疾,每個(gè) 模塊擁有自己的命名空間称近,不同模塊中相同名稱的符號(hào)不會(huì)發(fā)生沖突第队。
6.2 靜態(tài)庫(kù)中OC分類中的方法找不到運(yùn)行時(shí)奔潰
場(chǎng)景
假設(shè)一個(gè)靜態(tài)庫(kù),庫(kù)中有OC寫(xiě)的分類刨秆,但分類所屬的類定義不在庫(kù)中如NSString
凳谦。
在把這個(gè)靜態(tài)庫(kù)集成到工程里后,如果編譯設(shè)置other linker flags
沒(méi)有添加-ObjC
衡未,那么在使用這個(gè)OC分類的方法時(shí)尸执,就會(huì)在運(yùn)行時(shí)奔潰: unrecognized selector sent to class ..
原因
由于標(biāo)準(zhǔn)UNIX靜態(tài)庫(kù)的實(shí)現(xiàn)、鏈接器和Objective-C的動(dòng)態(tài)特性之間的問(wèn)題缓醋,出現(xiàn)了“選擇器未識(shí)別”運(yùn)行時(shí)異常如失。Objective-C并沒(méi)有為每個(gè)函數(shù)(或方法,在Objective-C中)定義鏈接器符號(hào)送粱,而是只為每個(gè)類生成鏈接器符號(hào)褪贵。如果使用類別擴(kuò)展預(yù)先存在的類,則鏈接器不知道如何將核心類實(shí)現(xiàn)的對(duì)象代碼與類別實(shí)現(xiàn)相關(guān)聯(lián)。這樣可以防止在生成的應(yīng)用程序中創(chuàng)建的對(duì)象響應(yīng)類別中定義的選擇器脆丁。
這也就是說(shuō)世舰,靜態(tài)庫(kù)中的OC分類的方法沒(méi)有與類關(guān)聯(lián)起來(lái)。
改法
如果是手動(dòng)集成這種靜態(tài)庫(kù)槽卫,需要在主工程中的other linker flags
添加-ObjC
跟压。
-ObjC參數(shù)的作用:
This flag causes the linker to load every object file in the library that defines an Objective-C class or category. While this option will typically result in a larger executable (due to additional object code loaded into the application), it will allow the successful creation of effective Objective-C static libraries that contain categories on existing classes.
此標(biāo)志使鏈接器加載庫(kù)中定義Objective-C類或類別的每個(gè)對(duì)象文件。雖然此選項(xiàng)通常會(huì)導(dǎo)致更大的可執(zhí)行文件(由于應(yīng)用程序中加載了額外的對(duì)象代碼)歼培,但它將允許成功創(chuàng)建有效的Objective-C靜態(tài)庫(kù)震蒋,其中包含現(xiàn)有類的類別。
有興趣可以了解具體原因 iOS靜態(tài)庫(kù)中類的分類問(wèn)題和符號(hào)沖突問(wèn)題躲庄。
另外喷好,如果使用的庫(kù)是動(dòng)態(tài)庫(kù),也不會(huì)有這個(gè)問(wèn)題读跷,原因是動(dòng)態(tài)庫(kù)是運(yùn)行時(shí)才鏈接的梗搅。
動(dòng)態(tài)庫(kù)是在運(yùn)行時(shí)被動(dòng)態(tài)加載到內(nèi)存中的,而不是在編譯時(shí)被靜態(tài)鏈接效览。這意味著在動(dòng)態(tài)庫(kù)加載時(shí)无切,它的所有代碼才會(huì)被加入到進(jìn)程的地址空間中。相比之下丐枉,靜態(tài)庫(kù)在編譯時(shí)就會(huì)被鏈接到可執(zhí)行文件中哆键,可能會(huì)引起加載的時(shí)機(jī)問(wèn)題。
CocoaPods集成靜態(tài)庫(kù)沒(méi)有這個(gè)問(wèn)題
如果是使用CocoaPods集成靜態(tài)庫(kù)瘦锹,那么會(huì)自動(dòng)給工程添加編譯參數(shù)-ObjC
, 所以使用CocoaPods集成的靜態(tài)庫(kù)中的OC分類不會(huì)有這個(gè)問(wèn)題籍嘹。
Pods-工程名.debug.xcconfig中
OTHER_LDFLAGS = $(inherited) -ObjC -framework "AFNetworking"
四、了解動(dòng)態(tài)庫(kù)
1. iOS中的動(dòng)態(tài)庫(kù)
系統(tǒng)提供的framework都是動(dòng)態(tài)庫(kù)類型弯院,比如UIKit.framework
辱士、libc++.tbd
;在集成庫(kù)時(shí)听绳,如果是靜態(tài)庫(kù)類型颂碘,那么靜態(tài)庫(kù)內(nèi)容最終是在主二進(jìn)制中;如果是動(dòng)態(tài)庫(kù)椅挣,會(huì)放在ipa
中的Frameworks
目錄头岔。
如果是cocoapods
集成庫(kù),那么如果在庫(kù)的spec中沒(méi)有指定s.static_framework = true
時(shí)鼠证,在podfile以下寫(xiě)法都是生成動(dòng)態(tài)庫(kù):
方法一:
use_frameworks!
方法二:
use_frameworks! :linkage => :dynamic #使用動(dòng)態(tài)鏈接
2. 動(dòng)態(tài)庫(kù)的格式介紹
.tbd
(Text-Based Stub Library)
.tbd 文件是一種文本格式的庫(kù)文件描述符峡竣,主要包含了庫(kù)的元數(shù)據(jù)信息,如符號(hào)列表量九、版本信息等适掰。
在 iOS 中,.tbd 文件通常用于描述系統(tǒng)框架和庫(kù),例如 iOS SDK 提供的框架攻谁。
這些文件并不包含實(shí)際的二進(jìn)制代碼稚伍,而是提供了一個(gè)輕量級(jí)的描述,用于編譯和鏈接時(shí)確定庫(kù)的接口和依賴關(guān)系戚宦。
在 Xcode 構(gòu)建過(guò)程中个曙,.tbd 文件會(huì)被用于生成實(shí)際的動(dòng)態(tài)庫(kù)鏈接信息。
.dylib
.dylib 文件是實(shí)際的動(dòng)態(tài)庫(kù)文件受楼,包含了編譯好的二進(jìn)制代碼垦搬、數(shù)據(jù)等。
在 iOS 中艳汽,.dylib 文件用于存儲(chǔ)動(dòng)態(tài)鏈接庫(kù)的實(shí)現(xiàn)猴贰,可以由系統(tǒng)或第三方提供。
這些文件是真正的共享庫(kù)河狐,運(yùn)行時(shí)動(dòng)態(tài)加載到應(yīng)用程序中米绕,提供所需的功能。
.framework
目錄結(jié)構(gòu)是規(guī)范化馋艺,是一種更為結(jié)構(gòu)化的庫(kù)格式栅干,用于更方便地組織和使用共享代碼和資源。
XCFramework
引入了對(duì)多平臺(tái)和多架構(gòu)的支持捐祠,可以包含適用于不同平臺(tái)和處理器架構(gòu)的二進(jìn)制版本碱鳞。類似于胖二進(jìn)制。
系統(tǒng)動(dòng)態(tài)庫(kù)
各種格式都有踱蛀。從 Xcode7 在導(dǎo)入系統(tǒng)動(dòng)態(tài)庫(kù)時(shí)窿给,可以發(fā)現(xiàn).dylib
文件變成了.tbd
文件。.tbd
文件相比.dylib
文件來(lái)說(shuō)包大小更小率拒,實(shí)際使用的還是dylib的二進(jìn)制代碼庫(kù)崩泡。
stackoverflow的回答
比如: libsqlite3.tbd
是個(gè)文本文件,其安裝名是libsqlite3.dylib
.
archs: [ armv7, armv7s, arm64 ]
platform: ios
install-name: /usr/lib/libsqlite3.dylib
current-version: 216.4
compatibility-version: 9.0
exports:
- archs: [ armv7, armv7s, arm64 ]
symbols: [ __sqlite3_lockstate, __sqlite3_purgeEligiblePagerCacheMemory,
__sqlite3_system_busy_handler, __sqlite_auto_profile,
...
So it appears that the .dylib file is the actual library of binary code that your project is using and is located in the /usr/lib/ directory on the user's device. The .tbd file, on the other hand, is just a text file that is included in your project and serves as a link to the required .dylib binary. Since this text file is much smaller than the binary library, it makes the SDK's download size smaller.
二方庫(kù)
我們創(chuàng)建的動(dòng)態(tài)庫(kù)一般是framework和xcframework格式俏橘。
3. 動(dòng)態(tài)庫(kù)的鏈接
Static frameworks are linked at compile time. Dynamic frameworks are linked at runtime
庫(kù)類型 | 鏈接時(shí)期截圖 |
---|---|
靜態(tài)庫(kù) | 靜態(tài)庫(kù).png
|
動(dòng)態(tài)庫(kù) | 動(dòng)態(tài)庫(kù).png
|
3. 動(dòng)態(tài)庫(kù)的優(yōu)缺點(diǎn)
系統(tǒng)的動(dòng)態(tài)庫(kù)是各個(gè)APP可以共享一份的允华,這樣能節(jié)省內(nèi)存。自己生成的動(dòng)態(tài)庫(kù)僅限于自己APP內(nèi)部共享寥掐,即和擴(kuò)展、插件等進(jìn)程共享磷蜀。
動(dòng)態(tài)庫(kù)也可支持設(shè)置啟動(dòng)時(shí)不加載召耘,在實(shí)際用到的時(shí)使用dlopen
加載;
http://www.reibang.com/p/08b0cb296278
缺點(diǎn):
動(dòng)態(tài)庫(kù)相比做成靜態(tài)庫(kù)褐隆,最終的ipa包體積會(huì)更大一點(diǎn)點(diǎn)污它;啟動(dòng)時(shí),動(dòng)態(tài)庫(kù)需要獨(dú)立加載并動(dòng)態(tài)鏈接符號(hào),所以啟動(dòng)耗時(shí)多衫贬。
五德澈、XCFramework
XCFramework 是蘋(píng)果新出的庫(kù)類型,在 Xcode 11 及 cocoapods 1.9 以上版本被支持固惯,與普通動(dòng)態(tài)庫(kù)/靜態(tài)庫(kù)最大的區(qū)別是將多個(gè)平臺(tái)(iOS, macOS, tvOS, watchOS, iPadOS, carPlayOS梆造,模擬器)的二進(jìn)制庫(kù),捆綁到一個(gè)可分發(fā)的.xcframework捆綁包中葬毫,支持所有的蘋(píng)果平臺(tái)和架構(gòu)镇辉。
對(duì)比使用 .framework 格式,使用 .xcframework 格式 APP 包大小和啟動(dòng)速度都有提升贴捡。
相關(guān)資料:
蘋(píng)果關(guān)于Mach-O的說(shuō)明文檔:
https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachOTopics/0-Introduction/introduction.html#//apple_ref/doc/uid/TP40001827-SW1
蘋(píng)果關(guān)于動(dòng)態(tài)庫(kù)的說(shuō)明文檔:
https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/OverviewOfDynamicLibraries.html#//apple_ref/doc/uid/TP40001873-SW1
蘋(píng)果關(guān)于靜態(tài)庫(kù)OC分類時(shí)的文檔:
Building Objective-C static libraries with categories:
網(wǎng)友總結(jié)文檔:
XCFramework 基礎(chǔ)-用腳本生成
iOS靜態(tài)庫(kù)中類的分類問(wèn)題和符號(hào)沖突問(wèn)題
iOS之深入解析靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)