什么是庫 ?
庫就是程序代碼的集合嫂易,將N個(gè)文件組織起來,是共享程序代碼的一種方式掐禁。庫從本質(zhì)上來說是一種可執(zhí)行代碼的二進(jìn)制格式怜械,可以被載入內(nèi)存中執(zhí)行。
庫的分類
開源庫:源代碼是公開的傅事,可以看到每個(gè)實(shí)現(xiàn)文件(.m文件)的實(shí)現(xiàn)缕允,例如GitHub上的常用的開源庫:AFNetworking、SDWebImage等享完;
-
閉源庫:不公開源代碼灼芭,是經(jīng)過編譯后的二進(jìn)制文件,看不到具體的實(shí)現(xiàn)般又。閉源庫又分為:靜態(tài)庫 和 動(dòng)態(tài)庫
1彼绷、linux中靜態(tài)庫和動(dòng)態(tài)庫區(qū)別:
庫從本質(zhì)上來說是一種可執(zhí)行代碼的
二進(jìn)制格式
,可以被載入內(nèi)存中執(zhí)行茴迁。庫分靜態(tài)庫和動(dòng)態(tài)庫兩種寄悯。靜態(tài)庫:這類庫的名字一般是libxxx.a;利用靜態(tài)函數(shù)庫編譯成的文件比較大堕义,因?yàn)檎麄€(gè)函數(shù)庫的所有數(shù)據(jù)都會(huì)被整合進(jìn)目標(biāo)代碼中猜旬,他的優(yōu)點(diǎn)就顯而易見了,即編譯后的執(zhí)行程序不需要外部的函數(shù)庫支持倦卖,因?yàn)樗惺褂玫暮瘮?shù)都已經(jīng)被編譯進(jìn)去了洒擦。當(dāng)然這也會(huì)成為他的缺點(diǎn),因?yàn)槿绻o態(tài)函數(shù)庫改變了怕膛,那么你的程序必須重新編譯熟嫩。
動(dòng)態(tài)庫:這類庫的名字一般是libxxx.so;相對于靜態(tài)函數(shù)庫,動(dòng)態(tài)函數(shù)庫在編譯的時(shí)候 并沒有被編譯進(jìn)目標(biāo)代碼中褐捻,你的程序執(zhí)行到相關(guān)函數(shù)時(shí)才調(diào)用該函數(shù)庫里的相應(yīng)函數(shù)掸茅,因此動(dòng)態(tài)函數(shù)庫所產(chǎn)生的可執(zhí)行文件比較小。由于函數(shù)庫沒有被整合進(jìn)你的程序柠逞,而是程序運(yùn)行時(shí)動(dòng)態(tài)的申請并調(diào)用昧狮,所以程序的運(yùn)行環(huán)境中必須提供相應(yīng)的庫。動(dòng)態(tài)函數(shù)庫的改變并不影響你的程序板壮,所以動(dòng)態(tài)函數(shù)庫的升級比較方便逗鸣。
2、iOS開發(fā)中靜態(tài)庫和動(dòng)態(tài)庫區(qū)別:
靜態(tài)庫和動(dòng)態(tài)庫是相對編譯期和運(yùn)行期的:靜態(tài)庫在程序編譯時(shí)會(huì)被鏈接到目標(biāo)代碼中,程序運(yùn)行時(shí)將不再需要改靜態(tài)庫慕购;而動(dòng)態(tài)庫在程序編譯時(shí)并不會(huì)被鏈接到目標(biāo)代碼中聊疲,只是在程序運(yùn)行時(shí)才被載入,因?yàn)樵诔绦蜻\(yùn)行期間還需要?jiǎng)討B(tài)庫的存在沪悲。
靜態(tài)庫 好處:
- 模塊化获洲,分工合作,提高了代碼的復(fù)用及核心技術(shù)的保密程度
- 避免少量改動(dòng)經(jīng)常導(dǎo)致大量的重復(fù)編譯連接
- 也可以重用殿如,注意不是共享使用
動(dòng)態(tài)庫 好處:
使用動(dòng)態(tài)庫贡珊,可以將最終可執(zhí)行文件體積縮小,將整個(gè)應(yīng)用程序分模塊涉馁,團(tuán)隊(duì)合作门岔,進(jìn)行分工,影響比較小
使用動(dòng)態(tài)庫烤送,多個(gè)應(yīng)用程序共享內(nèi)存中得同一份庫文件寒随,節(jié)省資源
使用動(dòng)態(tài)庫,可以不重新編譯連接可執(zhí)行程序的前提下帮坚,更新動(dòng)態(tài)庫文件達(dá)到更新應(yīng)用程序的目的妻往。
應(yīng)用插件化
軟件版本實(shí)時(shí)模塊升級
在其它大部分平臺(tái)上,動(dòng)態(tài)庫都可以用于不同應(yīng)用間共享试和, 共享可執(zhí)行文件讯泣,這就大大節(jié)省了內(nèi)存。
iOS平臺(tái) 在 iOS8 之前阅悍,蘋果不允許第三方框架使用動(dòng)態(tài)方式加載好渠,從 iOS8 開始允許開發(fā)者有條件地創(chuàng)建和使用動(dòng)態(tài)框架,這種框架叫做 Cocoa Touch Framework节视。雖然同樣是動(dòng)態(tài)框架拳锚,但是和系統(tǒng) framework 不同,app 中使用 Cocoa Touch Framework 制作的動(dòng)態(tài)庫 在打包和提交 app 時(shí)會(huì)被放到 app main bundle 的根目錄 中寻行,運(yùn)行在沙盒里晌畅,而不是系統(tǒng)中。也就是說寡痰,不同的 app 就算使用了同樣的 framework,但還是會(huì)有多份的框架被分別簽名棋凳,打包和加載拦坠。不過 iOS8 上開放了 App Extension 功能,可以為一個(gè)應(yīng)用創(chuàng)建插件剩岳,這樣主app和插件之間共享動(dòng)態(tài)庫還是可行的贞滨。
蘋果系統(tǒng)專屬的framework 是共享的(如UIKit), 但是我們自己使用 Cocoa Touch Framework 制作的動(dòng)態(tài)庫是放到 app bundle 中,運(yùn)行在沙盒中的
靜態(tài)庫和動(dòng)態(tài)庫的存在的形式
靜態(tài)庫:以.a 和 .framework為文件后綴名。
-
動(dòng)態(tài)庫:以.tbd(之前叫.dylib) 和 .framework 為文件后綴名晓铆。(系統(tǒng)直接提供給我們的framework都是動(dòng)態(tài)庫I琢肌)
理解:
.a
是一個(gè)純二進(jìn)制文件,.framework
中除了有二進(jìn)制文件之外還有資源文件骄噪。.a
尚困,要有.h
文件以及資源文件配合,.framework
文件可以直接使用链蕊∈绿穑總的來說,.a + .h + sourceFile = .framework
滔韵。所以創(chuàng)建靜態(tài)庫最好還是用.framework
的形式
靜態(tài)庫和動(dòng)態(tài)庫的區(qū)別
不同點(diǎn):
靜態(tài)庫在鏈接時(shí)逻谦,會(huì)被完整的復(fù)制到可執(zhí)行文件中,如果多個(gè)App都使用了同一個(gè)靜態(tài)庫陪蜻,那么每個(gè)App都會(huì)拷貝一份邦马,缺點(diǎn)是浪費(fèi)內(nèi)存。類似于定義一個(gè)基本變量宴卖,使用該基本變量是是新復(fù)制了一份數(shù)據(jù)滋将,而不是原來定義的;
-
動(dòng)態(tài)庫不會(huì)復(fù)制嘱腥,只有一份耕渴,程序運(yùn)行時(shí)動(dòng)態(tài)加載到內(nèi)存中,系統(tǒng)只會(huì)加載一次齿兔,多個(gè)程序共用一份橱脸,節(jié)約了內(nèi)存。類似于使用變量的內(nèi)存地址一樣分苇,使用的是同一個(gè)變量添诉;
共同點(diǎn):
靜態(tài)庫和動(dòng)態(tài)庫都是閉源庫,只能拿來滿足某個(gè)功能的使用医寿,不會(huì)暴露內(nèi)部具體的代碼信息
靜態(tài)庫的處理方式
-
對于一個(gè)靜態(tài)庫而言栏赴,其實(shí)已經(jīng)是編譯好的了,類似一個(gè) .o 的集合 這里并沒有連接谬以。在 build 的過程中只會(huì)參與鏈接的過程盯串,而這個(gè)鏈接的過程簡單的講就是合并寺旺,并且鏈接器只會(huì)將靜態(tài)庫中被使用的部分合并到可執(zhí)行文件中去森篷。相比較于動(dòng)態(tài)庫斗蒋,靜態(tài)庫的處理起來要簡單的多篮幢,具體如下圖:
image.png
鏈接器會(huì)將所有.o用到的 global symbol 和 unresolved symbol 放入一個(gè)臨時(shí)表卖哎,而且是 global symbol 是不能重復(fù)的筐骇。
對于靜態(tài)庫的 .o , 連接器會(huì)將沒有任何 symbol 在 unresolved symbol table 的給忽略惠拭。
unresolved symbol 類似
extern int test();
--- **.h **的 聲明?global symbol 類似
void test() { print("test")}
-- .m 的 實(shí)現(xiàn)?最后扩劝,鏈接器會(huì)用函數(shù)的實(shí)際地址來代替函數(shù)引用。
動(dòng)態(tài)庫的處理方式
- 首先,對于動(dòng)態(tài)庫而言其實(shí)分 動(dòng)態(tài)鏈接庫 和 動(dòng)態(tài)加載庫 兩種的棒呛,這兩個(gè)最本質(zhì)的區(qū)別還是加載時(shí)間聂示。
- 動(dòng)態(tài)鏈接庫:在沒有被加載到內(nèi)存的前提下,當(dāng)可執(zhí)行文件被加載簇秒,動(dòng)態(tài)庫也隨著被加載到內(nèi)存中鱼喉。在 Linked Framework and Libraries 設(shè)置的一些 share libraries≡姿【隨著程序啟動(dòng)而啟動(dòng)】
- 動(dòng)態(tài)加載庫:當(dāng)需要的時(shí)候再使用 dlopen 等通過代碼或者命令的方式來加載蒲凶。【在程序啟動(dòng)之后】
- 但是不論是哪種動(dòng)態(tài)庫拆内,相比較與靜態(tài)庫旋圆,動(dòng)態(tài)庫處理起來要棘手的多。由于動(dòng)態(tài)庫是動(dòng)態(tài)的麸恍,所以你事先不知道某個(gè)函數(shù)的具體地址灵巧。因此動(dòng)態(tài)鏈接器在鏈接函數(shù)的時(shí)候需要做大量的工作。
因?yàn)閯?dòng)態(tài)庫在鏈接函數(shù)需要做大量的工作抹沪,而靜態(tài)庫已經(jīng)實(shí)現(xiàn)處理好了刻肄。所以單純的在所有都沒有加載的情況下,靜態(tài)庫的加載速度會(huì)更快一點(diǎn)融欧。而在 iOS 開發(fā)中的『庫』(一) 提到的有所不妥敏弃,正確應(yīng)該是,雖然動(dòng)態(tài)庫更加耗時(shí)噪馏,但是對于在加載過的share libraries不需要再加載的這個(gè)前提下麦到,使用動(dòng)態(tài)庫可以節(jié)省一些啟動(dòng)時(shí)間。
- 而實(shí)現(xiàn)這個(gè)動(dòng)態(tài)鏈接是使用了 Procedure Linkage Table (PLT)欠肾。首先這個(gè) PLT 列出了程序中每一個(gè)函數(shù)的調(diào)用瓶颠,當(dāng)程序開始運(yùn)行,如果動(dòng)態(tài)庫被加載到內(nèi)存中刺桃,PLT 會(huì)去尋找動(dòng)態(tài)的地址并記錄下來粹淋,如果每個(gè)函數(shù)都被調(diào)用過的話,下一次調(diào)用就可以通過 PLT 直接跳轉(zhuǎn)了瑟慈,但是和靜態(tài)庫還是有點(diǎn)區(qū)別的是桃移,每一個(gè)函數(shù)的調(diào)用還是需要通過一張 PLT。這也正是 sunny 所說的所有靜態(tài)鏈接做的事情都搬到運(yùn)行時(shí)來做了葛碧,會(huì)導(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í)行文件(同時(shí)也包括多個(gè) .o 文件進(jìn)行 link)堆生。
相關(guān)動(dòng)態(tài)庫和靜態(tài)庫的創(chuàng)建
動(dòng)態(tài)庫動(dòng)態(tài)更新問題
能否動(dòng)態(tài)庫的方式來動(dòng)態(tài)更新AppStore上的版本呢?
framework本來是蘋果專屬的內(nèi)部提供的動(dòng)態(tài)庫文件格式雷酪,但是自從2014年WWDC之后淑仆,開發(fā)者也可以自定義創(chuàng)建framework實(shí)現(xiàn)動(dòng)態(tài)更新(繞過apple store審核,從服務(wù)器發(fā)布更新版本)的功能哥力,這與蘋果限定的上架的app必須經(jīng)過apple store的審核制度是沖突的蔗怠,所以含有自定義的framework的app是無法在商店上架的,但是如果開發(fā)的是企業(yè)內(nèi)部應(yīng)用吩跋,就可以考慮嘗試使用動(dòng)態(tài)更新技術(shù)來將多個(gè)獨(dú)立的app或者功能模塊集成在一個(gè)app上面D洹(我開發(fā)的就是企業(yè)內(nèi)部使用的app,我們將企業(yè)官網(wǎng)中的板塊開發(fā)成4個(gè)獨(dú)立的app锌钮,然后將其改造為framework文件最終集成在一款平臺(tái)級的app當(dāng)中進(jìn)行使用桥温,這樣就可以在一款app上面使用原本4個(gè)app的全部功能!)
** 使用自定義的動(dòng)態(tài)庫的方式來動(dòng)態(tài)更新只能用在 in house(企業(yè)發(fā)布) 和develop 模式卻但不能在使用到 AppStore **因?yàn)樵谏蟼鞔虬臅r(shí)候梁丘,蘋果會(huì)對我們的代碼進(jìn)行一次 Code Singing侵浸,包括 app 可執(zhí)行文件和所有Embedded 的動(dòng)態(tài)庫。因此兰吟,只要你修改了某個(gè)動(dòng)態(tài)庫的代碼通惫,并重新簽名,那么 MD5 的哈希值就會(huì)不一樣混蔼,在加載動(dòng)態(tài)庫的時(shí)候履腋,蘋果會(huì)檢驗(yàn)這個(gè) hash 值,當(dāng)蘋果監(jiān)測到這個(gè)動(dòng)態(tài)庫非法時(shí)惭嚣,就會(huì)造成 Crash
iOS 如何使用 framework 來進(jìn)行動(dòng)態(tài)更新遵湖!
重要參考文檔(一定要看):
談?wù)?Mach-O
- 在制作 framework 的時(shí)候需要選擇這個(gè) Mach-O Type.
- 為Mach Object文件格式的縮寫,它是一種用于可執(zhí)行文件晚吞,目標(biāo)代碼延旧,動(dòng)態(tài)庫,內(nèi)核轉(zhuǎn)儲(chǔ)的文件格式槽地。作為a.out格式的替代迁沫,Mach-O提供了更強(qiáng)的擴(kuò)展性芦瘾,并提升了符號表中信息的訪問速度。