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

- 在制作 framework 的時候需要選擇這個 Mach-O Type.
- 為Mach Object文件格式的縮寫,它是一種用於可執行文件,目標代碼,動態庫,內核轉儲的文件格式。作為a.out格式的替代,Mach-O提供了更強的擴展性,並提升了符號表中信息的訪問速度。