1. 程式人生 > >iOS 靜態庫和動態庫(庫詳解)

iOS 靜態庫和動態庫(庫詳解)

什麼是庫 ?

  庫就是程式程式碼的集合,將N個檔案組織起來,是共享程式程式碼的一種方式。庫從本質上來說是一種可執行程式碼的二進位制格式,可以被載入記憶體中執行。

庫的分類

  • 開源庫:原始碼是公開的,可以看到每個實現檔案(.m檔案)的實現,例如GitHub上的常用的開源庫:AFNetworking、SDWebImage等;
  • 閉源庫:不公開原始碼,是經過編譯後的二進位制檔案,看不到具體的實現。閉源庫又分為:靜態庫 和 動態庫

    1、linux中靜態庫和動態庫區別:

    庫從本質上來說是一種可執行程式碼的二進位制格式,可以被載入記憶體中執行。庫分靜態庫和動態庫兩種。

    靜態庫:這類庫的名字一般是libxxx.a;利用靜態函式庫編譯成的檔案比較大,因為整個函式庫的所有資料都會被整合進目的碼中,他的優點就顯而易見了,即編譯後的執行程式不需要外部的函式庫支援,因為所有使用的函式都已經被編譯進去了。當然這也會成為他的缺點,因為如果靜態函式庫改變了,那麼你的程式必須重新編譯。

    動態庫:這類庫的名字一般是libxxx.so;相對於靜態函式庫,動態函式庫在編譯的時候 並沒有被編譯進目的碼中,你的程式執行到相關函式時才呼叫該函式庫裡的相應函式,因此動態函式庫所產生的可執行檔案比較小。由於函式庫沒有被整合進你的程式,而是程式執行時動態的申請並呼叫,所以程式的執行環境中必須提供相應的庫。動態函式庫的改變並不影響你的程式,所以動態函式庫的升級比較方便。

    2、iOS開發中靜態庫和動態庫區別:

    靜態庫和動態庫是相對編譯期和執行期的:靜態庫在程式編譯時會被連結到目的碼中,程式執行時將不再需要改靜態庫;而動態庫在程式編譯時並不會被連結到目的碼中,只是在程式執行時才被載入,因為在程式執行期間還需要動態庫的存在。

    靜態庫 好處

    1. 模組化,分工合作,提高了程式碼的複用及核心技術的保密程度
    2. 避免少量改動經常導致大量的重複編譯連線
    3. 也可以重用,注意不是共享使用

    動態庫 好處:

    1. 使用動態庫,可以將最終可執行檔案體積縮小,將整個應用程式分模組,團隊合作,進行分工,影響比較小
    2. 使用動態庫,多個應用程式共享記憶體中得同一份庫檔案,節省資源
    3. 使用動態庫,可以不重新編譯連線可執行程式的前提下,更新動態庫檔案達到更新應用程式的目的。
    4. 應用外掛化
    5. 軟體版本實時模組升級
       
    6. 在其它大部分平臺上,動態庫都可以用於不同應用間共享, 共享可執行檔案,這就大大節省了記憶體。
      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**用到的 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)。
  • 相關動態庫和靜態庫的建立

  •   使用自定義的動態庫的方式來動態更新只能用在 in house(企業釋出) 和develop 模式卻但不能在使用到 AppStore 因為在上傳打包的時候,蘋果會對我們的程式碼進行一次 Code Singing,包括 app 可執行檔案和所有Embedded 的動態庫。因此,只要你修改了某個動態庫的程式碼,並重新簽名,那麼 MD5 的雜湊值就會不一樣,在載入動態庫的時候,蘋果會檢驗這個 hash 值,當蘋果監測到這個動態庫非法時,就會造成 Crash

    iOS 如何使用 framework 來進行動態更新!

    重要參考文件(一定要看):

  • 動態庫動態更新問題

    能否動態庫的方式來動態更新AppStore上的版本呢?

      framework本來是蘋果專屬的內部提供的動態庫檔案格式,但是自從2014年WWDC之後,開發者也可以自定義建立framework實現動態更新(繞過apple store稽核,從伺服器釋出更新版本)的功能,這與蘋果限定的上架的app必須經過apple store的稽核制度是衝突的,所以含有自定義的framework的app是無法在商店上架的,但是如果開發的是企業內部應用,就可以考慮嘗試使用動態更新技術來將多個獨立的app或者功能模組整合在一個app上面!(我開發的就是企業內部使用的app,我們將企業官網中的板塊開發成4個獨立的app,然後將其改造為framework檔案最終整合在一款平臺級的app當中進行使用,這樣就可以在一款app上面使用原本4個app的全部功能!)

  • Mach-O

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