編譯器之打包動態/靜態庫
一、簡介
I. 庫的類型分類
靜態庫
- 以 .a 或 .framework 為文件后綴名
.a 是一個二進制文件,不能直接拿來用,使用時需要 .a 文件 + 頭文件 + 資源文件
靜態庫打包時, 只能打包代碼 ,圖片文件、本地 JSON 文件和 xib 等資源文件無法打包進去 - 靜態庫 連接時 完整的復制到可執行文件中,多個文件使用,多份拷貝
- OC 建議使用靜態庫,OC 使用動態庫審核可能會不通過
- 靜態庫無法再包含其他的 .a 靜態庫。 只能把源碼放進去一起編譯
動態庫
- 以 .dylib 或 .framework 為文件后綴名「Xcode7 后 .dylib 變為 .tbd 文件」
.framework 文件,能直接拿來用。 .framework 文件 = 二進制文件(.a 文件 + .h 文件) + 資源文件
.tbd 文件,只是一個文本文件,其中包含架構信息,以及在真實運行時候二進制所在的位置,以及包含了動態庫的符號表還有類的一些信息,這些信息在編譯階段足夠了。「減少了所有設備 SDK 二進制動態庫的體積」 - 動態庫 運行時 動態加載到內存,只加載一次,多個文件公用,節省內存
- Swift 只能使用動態庫,不支持靜態庫
- 大部分第三方庫就是動態庫,可以暴露出來,放在源碼的外部引用使用
- 動態庫的特性使得 軟件版本實時模塊升級 、應用插件化、etc
II. iOS 設備的 CPU 架構
iOS 庫的打包,根據 CPU 架構的不同而不同
架構不同, 不能編譯通過
模擬器的 CPU 架構
- i386:iPhone 4s ~ 5
- x86_64:iPhone 5s ~ 7 Plus
真機的 CPU 架構
- armv6:iPhone、iPhone 2、iPhone 3G、iPod Touch「第一代」、iPod Touch「第二代」
- armv7:iPhone 3Gs、iPhone 4、iPhone 4s、iPad、iPad 2
- armv7s:iPhone 5、iPhone 5c 「靜態庫只要支持了 armv7,就可以在 armv7s 的架構上運行」
- armv64:iPhone 5s、iPhone 6、iPhone 6 Plus、iPhone 6s、iPhone 6s Plus、iPad Air、iPad Air2、iPad mini2、iPad mini3
二、打包靜態庫
I. .a 文件 靜態庫打包
-
創建靜態庫的工程
創建后執行 運行 或 編譯 ,都可以生成靜態庫「保存在項目的 Products 中」
-
通過在不同架構的設備下 編譯/運行 生成支持不同架構的靜態庫
-
在 編譯/運行 時, 所有項目 都可以設置是 Debug 還是 Release
- 設置庫的接口 頭文件
-
通過設置不僅在當前運行的 CPU 架構上,適配所有的機型號架構
一般 debug = no,release = yes 為了 debug 的時候編譯更快「只編譯連接當前的 CPU 架構所用的包」
可以控制 測試版本 和 發布版本 是否都不僅僅能在真機上使用
II. .framework 文件 靜態庫打包
-
前三步和 在 .a 文件 打包方式一致
-
設置庫的接口 頭文件
-
設置打包的是 靜態庫,因為動態庫也可以以 framework 的形式存在
設為Static Library「這個默認選項是動態的」
III. 使用靜態庫
1. 防止項目中的文件和 靜態庫的同名文件在運行時會覆蓋,只保留一張圖片
- 把圖片文件單獨的放在一個 .bundle 文件中「一般 .bundle 的名字和 .a 或 .framework 的名字相同」
- .bundle 文件制作方法:將文件夾,重命名為 XXX.bundle
同理,其他資源文件也放在一個 .bundle 中
2. 在使用 category 靜態庫的工程中,調用方法時,會出現找不到該方法的運行時錯誤: selector not recognized
原因:
- Unix 的標準靜態庫實現和 Objective-C 的動態特性之間有一些沖突
- Objective-C 沒有為每個函數定義鏈接符號,它 只為每個類創建鏈接符號
- 當在一個靜態庫中使用類別來擴展已有類的時候,鏈接器不知道如何把類原有的方法和類別中的方法整合起來
解決辦法:
- 在使用靜態庫的工程中配置 other linkerflags 的值為 -ObjC 如果還崩潰,在添加 -all_load 「或者 -force_load 」
- all_load 作用于所有的庫,而 -force_load 后面必須要指定具體的文件
這樣,會將靜態庫中所有和對象相關的文件都加載進來
3. 避免暴露過多的 .h 文件
使用
- 在靜態庫的內部創建一個 .h 文件「一般這個.h文件的名字和靜態庫的名字相同」
然后把所有需要暴露出來的 .h 文件名都集中放在這個 .h 文件中,只需要把這個 .h 暴露出來 - 一般這個總頭文件內部引入其他頭文件的格式為
#import <framework名字/其他頭文件.h> ,這是以調用這個庫的 項目的角度來寫的
搜索頭文件
-
Header Search Paths
系統的搜索路徑,用來引入項目中 沒有添加 的 header 文件
通過 #import <名稱.h> 來引入
-
User Header Search Paths
用戶的搜索路徑,用來引入項目中的 已添加 的 header 文件
通過 #import <名稱.h> 或 #import "名稱.h" 來引入
-
Always Search User Paths
Header Search Paths 作為系統級別路徑一定會被搜索
設 Always Search User Paths 為 YES,編譯器會先搜索 User Header Search Paths 路徑下的目錄
設 Always Search User Paths 為 NO,編譯器不會搜索 User Header Search Paths 路徑下的目錄「默認」
-
設置格式舉例: $(SRCROOT)/項目名稱/AppDelegate.h
$(SRCROOT) 宏和 $(PROJECT_DIR) 宏都指 xxx.xcodeproj 所在的 父目錄 ,新建項目后的目錄為:
-
Project的 Building Settings 中得設置 默認 并不被 Targets 繼承
只有 Targets 的設置加入了 $(inherited) 時才被繼承,添加目錄的時候寫上 $(inherited) 就表示從 frameworks 里面讀取
-
設置查找路徑的參數
recursive:遍歷該目錄,編譯的時候在找庫的路徑的時候,會遍歷該目錄下的所有子目錄的庫文件
non-recursive:不遍歷該目錄「默認,推薦使用,可減少編譯速度」
4. 拖拽項目/將項目當做靜態庫處理
- 給當前項目在 Link Binary With Libraries 中添加拖拽項目的 庫文件
- 如果當前的 Target 需要依賴其他庫文件,在 Target Dependencies 中添加所需的庫文件
- 如果庫文件在 Link Binary With Libraries 中已經添加 「必要」 ,但也可能需要在 Target Dependencies 中再次添加 「非必要」
5. 其他
-
查看靜態庫所支持的 CPU 架構
在命令行找到靜態庫所在的文件夾,執行 lipo -info 靜態庫文件名 命令
-
合并靜態庫
在命令行找到靜態庫所在的文件夾,執行 lipo -create 靜態庫1 靜態庫2 -output 新靜態庫名稱.a 命令
三、打包動態庫
1. 制作、編譯過程與靜態庫相同
2. 在 Embedded Binaries 中添加動態庫
Paste_Image.png
3. 合并動態庫文件
合并動態庫文件并非合并的是 .framework 文件,而是其中的二進制代碼文件
4. 動態庫的使用注意
I. 自定義的 .a 靜態庫,不可以包含動態庫
II. 自定義的 .framework 靜態庫,可以包含動態庫文件
繞過 Xcode 的 UI 界面來連接動態庫,步驟如下
-
項目中不需要引用連接的動態庫「.tbd 的動態庫」
-
通過格式: -l<library_name> 添加動態庫,例:添加 libiconv.tdb 記為 -liconv
缺陷
-
雖然自定義的靜態庫中已經導入了動態庫 X.tbd ,但是,當自定義的 framework 的靜態庫被調用時,可能需要再次導入動態庫 X.tbd 到當前項目中
來自:http://www.jianshu.com/p/543965d30c16