什么是庫 ?
庫就是程序代碼的集合,將N個文件組織起來,是共享程序代碼的一種方式。庫從本質(zhì)上來說是一種可執(zhí)行代碼的二進(jìn)制格式,可以被載入內(nèi)存中執(zhí)行。
庫的分類
開源庫:源代碼是公開的,可以看到每個實(shí)現(xiàn)文件(.m文件)的實(shí)現(xiàn),例如GitHub上的常用的開源庫:AFNetworking、SDWebImage等;
-
閉源庫:不公開源代碼,是經(jīng)過編譯后的二進(jìn)制文件,看不到具體的實(shí)現(xiàn)。閉源庫又分為:靜態(tài)庫 和 動態(tài)庫
1、linux中靜態(tài)庫和動態(tài)庫區(qū)別:
庫從本質(zhì)上來說是一種可執(zhí)行代碼的
二進(jìn)制格式,可以被載入內(nèi)存中執(zhí)行。庫分靜態(tài)庫和動態(tài)庫兩種。靜態(tài)庫:這類庫的名字一般是libxxx.a;利用靜態(tài)函數(shù)庫編譯成的文件比較大,因?yàn)檎麄€函數(shù)庫的所有數(shù)據(jù)都會被整合進(jìn)目標(biāo)代碼中,他的優(yōu)點(diǎn)就顯而易見了,即編譯后的執(zhí)行程序不需要外部的函數(shù)庫支持,因?yàn)樗惺褂玫暮瘮?shù)都已經(jīng)被編譯進(jìn)去了。當(dāng)然這也會成為他的缺點(diǎn),因?yàn)槿绻o態(tài)函數(shù)庫改變了,那么你的程序必須重新編譯。
動態(tài)庫:這類庫的名字一般是libxxx.so;相對于靜態(tài)函數(shù)庫,動態(tài)函數(shù)庫在編譯的時候 并沒有被編譯進(jìn)目標(biāo)代碼中,你的程序執(zhí)行到相關(guān)函數(shù)時才調(diào)用該函數(shù)庫里的相應(yīng)函數(shù),因此動態(tài)函數(shù)庫所產(chǎn)生的可執(zhí)行文件比較小。由于函數(shù)庫沒有被整合進(jìn)你的程序,而是程序運(yùn)行時動態(tài)的申請并調(diào)用,所以程序的運(yùn)行環(huán)境中必須提供相應(yīng)的庫。動態(tài)函數(shù)庫的改變并不影響你的程序,所以動態(tài)函數(shù)庫的升級比較方便。
2、iOS開發(fā)中靜態(tài)庫和動態(tài)庫區(qū)別:
靜態(tài)庫和動態(tài)庫是相對編譯期和運(yùn)行期的:靜態(tài)庫在程序編譯時會被鏈接到目標(biāo)代碼中,程序運(yùn)行時將不再需要改靜態(tài)庫;而動態(tài)庫在程序編譯時并不會被鏈接到目標(biāo)代碼中,只是在程序運(yùn)行時才被載入,因?yàn)樵诔绦蜻\(yùn)行期間還需要動態(tài)庫的存在。
靜態(tài)庫 好處:
- 模塊化,分工合作,提高了代碼的復(fù)用及核心技術(shù)的保密程度
- 避免少量改動經(jīng)常導(dǎo)致大量的重復(fù)編譯連接
- 也可以重用,注意不是共享使用
動態(tài)庫 好處:
使用動態(tài)庫,可以將最終可執(zhí)行文件體積縮小,將整個應(yīng)用程序分模塊,團(tuán)隊合作,進(jìn)行分工,影響比較小
使用動態(tài)庫,多個應(yīng)用程序共享內(nèi)存中得同一份庫文件,節(jié)省資源
使用動態(tài)庫,可以不重新編譯連接可執(zhí)行程序的前提下,更新動態(tài)庫文件達(dá)到更新應(yīng)用程序的目的。
應(yīng)用插件化
軟件版本實(shí)時模塊升級
在其它大部分平臺上,動態(tài)庫都可以用于不同應(yīng)用間共享, 共享可執(zhí)行文件,這就大大節(jié)省了內(nèi)存。
iOS平臺 在 iOS8 之前,蘋果不允許第三方框架使用動態(tài)方式加載,從 iOS8 開始允許開發(fā)者有條件地創(chuàng)建和使用動態(tài)框架,這種框架叫做 Cocoa Touch Framework。雖然同樣是動態(tài)框架,但是和系統(tǒng) framework 不同,app 中使用 Cocoa Touch Framework 制作的動態(tài)庫 在打包和提交 app 時會被放到 app main bundle 的根目錄 中,運(yùn)行在沙盒里,而不是系統(tǒng)中。也就是說,不同的 app 就算使用了同樣的 framework,但還是會有多份的框架被分別簽名,打包和加載。不過 iOS8 上開放了 App Extension 功能,可以為一個應(yīng)用創(chuàng)建插件,這樣主app和插件之間共享動態(tài)庫還是可行的。
蘋果系統(tǒng)專屬的framework 是共享的(如UIKit), 但是我們自己使用 Cocoa Touch Framework 制作的動態(tài)庫是放到 app bundle 中,運(yùn)行在沙盒中的
靜態(tài)庫和動態(tài)庫的存在的形式
靜態(tài)庫:以.a 和 .framework為文件后綴名。
-
動態(tài)庫:以.tbd(之前叫.dylib) 和 .framework 為文件后綴名。(系統(tǒng)直接提供給我們的framework都是動態(tài)庫!)
理解:
.a是一個純二進(jìn)制文件,.framework中除了有二進(jìn)制文件之外還有資源文件。.a,要有.h文件以及資源文件配合,.framework文件可以直接使用。總的來說,.a + .h + sourceFile = .framework。所以創(chuàng)建靜態(tài)庫最好還是用.framework的形式
靜態(tài)庫和動態(tài)庫的區(qū)別
不同點(diǎn):
靜態(tài)庫在鏈接時,會被完整的復(fù)制到可執(zhí)行文件中,如果多個App都使用了同一個靜態(tài)庫,那么每個App都會拷貝一份,缺點(diǎn)是浪費(fèi)內(nèi)存。類似于定義一個基本變量,使用該基本變量是是新復(fù)制了一份數(shù)據(jù),而不是原來定義的;
-
動態(tài)庫不會復(fù)制,只有一份,程序運(yùn)行時動態(tài)加載到內(nèi)存中,系統(tǒng)只會加載一次,多個程序共用一份,節(jié)約了內(nèi)存。類似于使用變量的內(nèi)存地址一樣,使用的是同一個變量;
共同點(diǎn):
靜態(tài)庫和動態(tài)庫都是閉源庫,只能拿來滿足某個功能的使用,不會暴露內(nèi)部具體的代碼信息
靜態(tài)庫的處理方式
-
對于一個靜態(tài)庫而言,其實(shí)已經(jīng)是編譯好的了,類似一個 .o 的集合 這里并沒有連接。在 build 的過程中只會參與鏈接的過程,而這個鏈接的過程簡單的講就是合并,并且鏈接器只會將靜態(tài)庫中被使用的部分合并到可執(zhí)行文件中去。相比較于動態(tài)庫,靜態(tài)庫的處理起來要簡單的多,具體如下圖:
image.png
鏈接器會將所有.o用到的 global symbol 和 unresolved symbol 放入一個臨時表,而且是 global symbol 是不能重復(fù)的。
對于靜態(tài)庫的 .o , 連接器會將沒有任何 symbol 在 unresolved symbol table 的給忽略。
unresolved symbol 類似
extern int test();--- **.h **的 聲明?global symbol 類似
void test() { print("test")}-- .m 的 實(shí)現(xiàn)?最后,鏈接器會用函數(shù)的實(shí)際地址來代替函數(shù)引用。
動態(tài)庫的處理方式
- 首先,對于動態(tài)庫而言其實(shí)分 動態(tài)鏈接庫 和 動態(tài)加載庫 兩種的,這兩個最本質(zhì)的區(qū)別還是加載時間。
- 動態(tài)鏈接庫:在沒有被加載到內(nèi)存的前提下,當(dāng)可執(zhí)行文件被加載,動態(tài)庫也隨著被加載到內(nèi)存中。在 Linked Framework and Libraries 設(shè)置的一些 share libraries?!倦S著程序啟動而啟動】
- 動態(tài)加載庫:當(dāng)需要的時候再使用 dlopen 等通過代碼或者命令的方式來加載?!驹诔绦騿又蟆?/li>
- 但是不論是哪種動態(tài)庫,相比較與靜態(tài)庫,動態(tài)庫處理起來要棘手的多。由于動態(tài)庫是動態(tài)的,所以你事先不知道某個函數(shù)的具體地址。因此動態(tài)鏈接器在鏈接函數(shù)的時候需要做大量的工作。
因?yàn)閯討B(tài)庫在鏈接函數(shù)需要做大量的工作,而靜態(tài)庫已經(jīng)實(shí)現(xiàn)處理好了。所以單純的在所有都沒有加載的情況下,靜態(tài)庫的加載速度會更快一點(diǎn)。而在 iOS 開發(fā)中的『庫』(一) 提到的有所不妥,正確應(yīng)該是,雖然動態(tài)庫更加耗時,但是對于在加載過的share libraries不需要再加載的這個前提下,使用動態(tài)庫可以節(jié)省一些啟動時間。
- 而實(shí)現(xiàn)這個動態(tài)鏈接是使用了 Procedure Linkage Table (PLT)。首先這個 PLT 列出了程序中每一個函數(shù)的調(diào)用,當(dāng)程序開始運(yùn)行,如果動態(tài)庫被加載到內(nèi)存中,PLT 會去尋找動態(tài)的地址并記錄下來,如果每個函數(shù)都被調(diào)用過的話,下一次調(diào)用就可以通過 PLT 直接跳轉(zhuǎn)了,但是和靜態(tài)庫還是有點(diǎn)區(qū)別的是,每一個函數(shù)的調(diào)用還是需要通過一張 PLT。這也正是 sunny 所說的所有靜態(tài)鏈接做的事情都搬到運(yùn)行時來做了,會導(dǎo)致更慢 的原因。
從源代碼到app
當(dāng)我們點(diǎn)擊了 build 之后,做了什么事情呢?
- 預(yù)處理(Pre-process):把宏替換,刪除注釋,展開頭文件,產(chǎn)生 .i 文件。
- 編譯(Compliling):把之前的 .i 文件轉(zhuǎn)換成匯編語言,產(chǎn)生 .s文件。
- 匯編(Asembly):把匯編語言文件轉(zhuǎn)換為機(jī)器碼文件,產(chǎn)生 .o 文件。
- 鏈接(Link):對.o文件中的對于其他的庫的引用的地方進(jìn)行引用,生成最后的可執(zhí)行文件(同時也包括多個 .o 文件進(jìn)行 link)。
相關(guān)動態(tài)庫和靜態(tài)庫的創(chuàng)建
動態(tài)庫動態(tài)更新問題
能否動態(tài)庫的方式來動態(tài)更新AppStore上的版本呢?
framework本來是蘋果專屬的內(nèi)部提供的動態(tài)庫文件格式,但是自從2014年WWDC之后,開發(fā)者也可以自定義創(chuàng)建framework實(shí)現(xiàn)動態(tài)更新(繞過apple store審核,從服務(wù)器發(fā)布更新版本)的功能,這與蘋果限定的上架的app必須經(jīng)過apple store的審核制度是沖突的,所以含有自定義的framework的app是無法在商店上架的,但是如果開發(fā)的是企業(yè)內(nèi)部應(yīng)用,就可以考慮嘗試使用動態(tài)更新技術(shù)來將多個獨(dú)立的app或者功能模塊集成在一個app上面?。ㄎ议_發(fā)的就是企業(yè)內(nèi)部使用的app,我們將企業(yè)官網(wǎng)中的板塊開發(fā)成4個獨(dú)立的app,然后將其改造為framework文件最終集成在一款平臺級的app當(dāng)中進(jìn)行使用,這樣就可以在一款app上面使用原本4個app的全部功能!)
** 使用自定義的動態(tài)庫的方式來動態(tài)更新只能用在 in house(企業(yè)發(fā)布) 和develop 模式卻但不能在使用到 AppStore **因?yàn)樵谏蟼鞔虬臅r候,蘋果會對我們的代碼進(jìn)行一次 Code Singing,包括 app 可執(zhí)行文件和所有Embedded 的動態(tài)庫。因此,只要你修改了某個動態(tài)庫的代碼,并重新簽名,那么 MD5 的哈希值就會不一樣,在加載動態(tài)庫的時候,蘋果會檢驗(yàn)這個 hash 值,當(dāng)蘋果監(jiān)測到這個動態(tài)庫非法時,就會造成 Crash
iOS 如何使用 framework 來進(jìn)行動態(tài)更新!
重要參考文檔(一定要看):
談?wù)?Mach-O

- 在制作 framework 的時候需要選擇這個 Mach-O Type.
- 為Mach Object文件格式的縮寫,它是一種用于可執(zhí)行文件,目標(biāo)代碼,動態(tài)庫,內(nèi)核轉(zhuǎn)儲的文件格式。作為a.out格式的替代,Mach-O提供了更強(qiáng)的擴(kuò)展性,并提升了符號表中信息的訪問速度。
