1. 程式人生 > >IOS-靜態庫,動態庫與 Framework

IOS-靜態庫,動態庫與 Framework

靜態庫與動態庫的區別

首先來看什麼是庫,庫(Library)說白了就是一段編譯好的二進位制程式碼,加上標頭檔案就可以供別人使用。
什麼時候我們會用到庫呢?一種情況是某些程式碼需要給別人使用,但是我們不希望別人看到原始碼,就需要以庫的形式進行封裝,只暴露出標頭檔案。另外一種情況是,對於某些不會進行大的改動的程式碼,我們想減少編譯的時間,就可以把它打包成庫,因為庫是已經編譯好的二進位制了,編譯的時候只需要 Link 一下,不會浪費編譯時間。
上面提到庫在使用的時候需要 Link,Link 的方式有兩種,靜態和動態,於是便產生了靜態庫和動態庫。

靜態庫

靜態庫即靜態連結庫(Windows 下的 .lib,Linux 和 Mac 下的 .a)。之所以叫做靜態,是因為靜態庫在編譯的時候會被直接拷貝一份,複製到目標程式裡,這段程式碼在目標程式裡就不會再改變了。
靜態庫的好處很明顯,編譯完成之後,庫檔案實際上就沒有作用了。目標程式沒有外部依賴,直接就可以執行。當然其缺點也很明顯,就是會使用目標程式的體積增大。

動態庫

動態庫即動態連結庫(Windows 下的 .dll,Linux 下的 .so,Mac 下的 .dylib)。與靜態庫相反,動態庫在編譯時並不會被拷貝到目標程式中,目標程式中只會儲存指向動態庫的引用。等到程式執行時,動態庫才會被真正載入進來。
動態庫的優點是,不需要拷貝到目標程式中,不會影響目標程式的體積,而且同一份庫可以被多個程式使用(因為這個原因,動態庫也被稱作共享庫)。同時,編譯時才載入的特性,也可以讓我們隨時對庫進行替換,而不需要重新編譯程式碼。動態庫帶來的問題主要是,動態載入會帶來一部分效能損失,使用動態庫也會使得程式依賴於外部環境。如果環境缺少動態庫或者庫的版本不正確,就會導致程式無法執行(Linux 下喜聞樂見的 lib not found 錯誤)。

iOS Framework

除了上面提到的 .a 和 .dylib 之外,Mac OS/iOS 平臺還可以使用 Framework。Framework 實際上是一種打包方式,將庫的二進位制檔案,標頭檔案和有關的資原始檔打包到一起,方便管理和分發。
在 iOS 8 之前,iOS 平臺不支援使用動態 Framework,開發者可以使用的 Framework 只有蘋果自家的 UIKit.Framework,Foundation.Framework 等。這種限制可能是出於安全的考慮(見這裡的討論)。換一個角度講,因為 iOS 應用都是執行在沙盒當中,不同的程式之間不能共享程式碼,同時動態下載程式碼又是被蘋果明令禁止的,沒辦法發揮出動態庫的優勢,實際上動態庫也就沒有存在的必要了。
由於上面提到的限制,開發者想要在 iOS 平臺共享程式碼,唯一的選擇就是打包成靜態庫 .a 檔案,同時附上標頭檔案(例如微信的SDK)。但是這樣的打包方式不夠方便,使用時也比較麻煩,大家還是希望共享程式碼都能能像 Framework 一樣,直接扔到工程裡就可以用。於是人們想出了各種奇技淫巧去讓 Xcode Build 出 iOS 可以使用的 Framework,具體做法參考這裡和這裡,這種方法產生的 Framework 還有 “偽”(Fake) Framework 和 “真“(Real) Framework 的區別。
iOS 8/Xcode 6 推出之後,iOS 平臺添加了動態庫的支援,同時 Xcode 6 也原生自帶了 Framework 支援(動態和靜態都可以),上面提到的的奇技淫巧也就沒有必要了(新的做法參考這裡)。為什麼 iOS 8 要新增動態庫的支援?唯一的理由大概就是 Extension 的出現。Extension 和 App 是兩個分開的可執行檔案,同時需要共享程式碼,這種情況下動態庫的支援就是必不可少的了。但是這種動態 Framework 和系統的 UIKit.Framework 還是有很大區別。系統的 Framework 不需要拷貝到目標程式中,我們自己做出來的 Framework 哪怕是動態的,最後也還是要拷貝到 App 中(App 和 Extension 的 Bundle 是共享的),因此蘋果又把這種 Framework 稱為 Embedded Framework。

Swift 支援

跟著 iOS8 / Xcode 6 同時釋出的還有 Swift。如果要在專案中使用外部的程式碼,可選的方式只有兩種,一種是把程式碼拷貝到工程中,另一種是用動態 Framework。使用靜態庫是不支援的。
造成這個問題的原因主要是 Swift 的 Runtime 沒有被包含在 iOS 系統中,而是會打包進 App 中(這也是造成 Swift App 體積大的原因),靜態庫會導致最終的目標程式中包含重複的 Runtime(這是蘋果自家的解釋)。同時拷貝 Runtime 這種做法也會導致在純 ObjC 的專案中使用 Swift 庫出現問題。蘋果聲稱等到 Swift 的 Runtime 穩定之後會被加入到系統當中,到時候這個限制就會被去除了(參考這個問題 的問題描述,也是來自蘋果自家文件)。

CocoaPods 的做法

在純 ObjC 的專案中,CocoaPods 使用編譯靜態庫 .a 方法將程式碼整合到專案中。在 Pods 專案中的每個 target 都對應這一個 Pod 的靜態庫。不過在編譯過程中並不會真的產出 .a 檔案。如果需要 .a 檔案的話,可以參考這裡,或者使用 CocoasPods-Packager這個外掛。
當不想釋出程式碼的時候,也可以使用 Framework 釋出 Pod,CocoaPods 提供了vendored_framework 選項來使用第三方 Framework,具體的做法可以參考這裡和這裡。
對於 Swift 專案,CocoaPods 提供了動態 Framework 的支援,通過use_frameworks! 選項控制。
更多有關程式碼分發的擴充套件資料可以參考這篇部落格: http://geeklu.com/2014/02/objc-lib/