一、構建步驟
創建一個動態庫 MyDynamicFramework

創建一個測試類

在 MyDynamicFramework.h(默認生成,可統一暴露頭文件) 中 #import "Person.h"
#import <UIKit/UIKit.h> //! Project version number for MyDynamicFramework.
FOUNDATION_EXPORT double MyDynamicFrameworkVersionNumber;
//! Project version string for MyDynamicFramework.
FOUNDATION_EXPORT const unsigned char MyDynamicFrameworkVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <MyDynamicFramework/PublicHeader.h>
#import "Person.h"點擊工程 -> Targets -> Build Phases -> Headers。
動態庫中新建的文件會自動添加到 project 列表,MyDynamicFramework.h 文件是處於 Public 列表中。由於動態庫外部使用者需要調用 Person.h 中的方法,所以也需要將 Person.h 拖拽到 Public 列表。

編譯動態庫
選擇動態庫對應的 Scheme,選擇 Generic iOS Device 或真機編譯出對應真機的動態庫,Command + B 編譯。在 Xcode 工程中的 Products(這個目錄不是工程源文件目錄,而是編譯后生成對應的沙盒目錄)找到 MyDynamicFramework.framework 文件,右鍵 show in finder。

利用 lipo -info 查看動態庫所支持的 CPU 指令集。

新建工程后所編譯出來的動態庫所支持的 CPU 指令集是 arm 7、arm64。
需要注意:
lipo -info [文件]后面跟的是文件路徑,而不是 .framework 路徑。
指令集種類
- armv7|armv7s|arm64 都是 ARM 處理器的指令集
- i386|x86_64 是 iOS 模擬器的指令集
理論上指令集是向下兼容的,比如連接設備為 arm64,那么是有可能編譯出的動態庫所支持的指令集為 armv7s 或者是 armv7。但是向下兼容並不是說一個 armv7s 的動態庫可以用在 arm64 架構的設備上,如果連接的設備是 arm64 的,而導入的動態庫是沒有支持 arm64,那么在編譯階段即會報錯。
Xcode 指令集的編譯選項
打開 Target -> Build Setting -> Architectures

- Architectures:指明選定 Target 要求被編譯生成的二進制包所支持的指令集
- Build Active Architecture Only:指明是否只編譯當前連接設備所支持的指令集,如果為 YES,那么只編譯出連接設備所對應的指令集;如果為 NO,則編譯出所有其它有效的指令集(由 Architectures 和 Valid Architectures決定)
- Valid Architectures:指明可能支持的指令集並非 Architectures 列表中指明的指令集都會被支持
編譯產生的動態庫所支持的指令集將由上面三個編譯選項所影響,首先一個動態庫要成功編譯,則需要這三個編譯選項的交集不為空。
制作支持各機型的動態庫
- Build Active Architecture Only 統一為 NO
- Architectures 和 Valid Architectures 都設置為 armv7、armv7s、arm64、arm64e
真機 Command + B 則生成支持 armv7、armv7s、arm64 的動態庫,模擬器運行,則生成支持 i386、x86_64 的動態庫。
- Build Active Architecture Only 統一為 NO
合並模擬器和真機動態庫

使用 lipo -create -output 命令合動態庫,注意路徑是文件路徑,不是 .framework 的路徑。
使用腳本合並
- 新建一個 target 腳本。

- 粘貼以下腳本內容到指定位置

if [ "${ACTION}" = "build" ] then
INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}.framework
DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework
SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
#ditto "${DEVICE_DIR}/Headers" "${INSTALL_DIR}/Headers"
# 使用lipo命令將其合並成一個通用framework
# 最后將生成的通用framework放置在工程根目錄下新建的Products目錄下
lipo -create "${DEVICE_DIR}/${PROJECT_NAME}" "${SIMULATOR_DIR}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}"
#open "${DEVICE_DIR}"
#open "${SRCROOT}/Products"
fi- 編譯新 target
- 編譯完成后生成的 framework 位於工程源代碼根目錄下的 Products 文件夾下面,通過 lipo -info 可以看到動態庫已經支持 i386、x86_64、armv7、armv7s、arm64。
注意:是工程目錄,不是沙盒目錄。


使用動態庫
在新工程的 target -> General -> Embedded Binaries 中添加 MyDynamicFramework.framework。

二、動態使用
2.1 使用別人提供的動態庫遇到的坑
①、第三方庫所支持的 CPU 指令集不全。
②、運行過程中出現 image not found 異常或者控制台沒有異常輸出。
原因:沒有往 Embedded Binaries 中添加 xxx.framework
2.2 動態庫動態更新問題
能否用動態庫來動態更新 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。
2.3 iOS 如何使用 framework 來進行動態更新?
重要參考文檔:iOS 利用 Framework 進行動態更新
2.4 談談 Mach-O
- 在制作 framework 的時候需要選擇這個 Mach-O Type,確定 static、dynamic 類型庫.
- 為 Mach Object 文件格式的縮寫,它是一種用於可執行文件,目標代碼、動態庫、內核轉儲的文件格式。作為 a.out 格式的替代,Mach-O 提供了更強的擴展性,並提升了符號表中信息的訪問速度。
2.5 自己創建的動態庫
自建的動態庫和系統的動態庫有什么區別呢?
我們創建的動態庫是在自己應用的 .app 目錄里面,只能自己的 App Extension 和 APP 使用。而系統的動態庫是在系統目錄里面,所有的程序都能使用。
可執行文件和自己創建的動態庫位置:
一般我們得到的 iOS 程序包是 .ipa 文件。其實就是一個壓縮包,解壓縮 .ipa 后里面會有一個 payload 文件夾,文件夾里有一個 .app 文件,右鍵顯示包內容,然后找到一個一般體積最大的、與 .app 同名的文件,那個文件就是可執行文件。

在模擬器上運行的時候用 [[NSBundle mainBundle] bundlePath]; 就能得到 .app 的路徑。可執行文件就在 .app 里面。
而我們自己創建的動態庫就在 .app 目錄下的 Framework 文件夾里。

我們可以看一下可執行文件中對動態庫的鏈接地址。用MachOView查看可執行文件。其中 @rpth 這個路徑表示的位置可以查看Xcode 中的鏈接路徑問題,而現在表示的其實就是 .app 下的 Framework 文件夾。


下圖表示了靜態庫、自建的動態庫和系統動態庫:

