目前我們公司所有的SDK都采用了framework的方式,不再采取靜態庫的姿勢,所以我今天也利用工作之余的時間學習一下這個創建framework的姿勢,雖然這個過程非常非常簡單,但是沒人指導你,自己摸索還是需要浪費點時間的,走更著我的腳步一起去看看怎么打包自己的第一個 framework.
先聲明本文都屬于操作部分,并且最基礎,至于理論部分是參考文檔的,還有其他的理論都會在后面更新!
理論基礎: 大神請跳過
1. 庫
庫是源代碼經過編譯,形成的二進制代碼,別人項目中使用我們的庫的時候,庫在參與編譯的時候,直接link就OK了,按照link的方式,可以把庫分為靜態庫和動態庫
2. 靜態庫
靜態庫在編譯的時候會被直接拷貝一份,復制到目標程序里,這段代碼在目標程序里就不會再改變了。
一般以.a 和 .framework為文件后綴名
這種做法是犧牲應用“體量”來節省編譯時間。
3. 動態庫
與靜態庫相反,動態庫在編譯時并不會被拷貝到目標程序中,目標程序中只會存儲指向動態庫的引用。等到程序運行時,動態庫才會被真正加載進來。
動態庫的優點是,不需要拷貝到目標程序中,不會影響目標程序的體積,而且同一份庫可以被多個程序使用(因為這個原因,動態庫也被稱作共享庫)。
同時,編譯時才載入的特性,也可以讓我們隨時對庫進行替換,而不需要重新編譯代碼。動態庫帶來的問題主要是,動態載入會帶來一部分性能損失,使用動態庫也會使得程序依賴于外部環境。如果環境缺少動態庫或者庫的版本不正確,就會導致程序無法運行
以.tbd(之前叫.dylib) 和 .framework 為文件后綴名
蘋果系統為我們提供了很多動態鏈接庫,我們可以在我們項目工程中查看一下
4. Framework
Framework 是一種打包方式,將庫的二進制文件,頭文件和有關的資源文件打包到一起,方便管理和分發。
Framework只是一種打包方式,其本身和靜態、動態無關!
但Cocoa Touch Framework 的實際內容為 Header + 動態鏈接庫 + 資源文件
5. 對Framework認識的誤區
誤區①:.framework是動態庫,.a是靜態庫,前面已經講過,不再贅述
誤區②:有人說“自定義的動態庫蘋果審核不通過”,那我打的framework是不是通不過審核?
任何沒有時間前提的結論都是耍流氓!!!
在 iOS 8 / iOS6之前,iOS 平臺不支持使用動態 Framework,開發者可以使用的 Framework 只有系統的framework,這種限制可能是出于安全的考慮
換一個角度講,因為 iOS 應用都是運行在沙盒當中,不同的程序之間不能共享代碼,同時動態下載代碼又是被蘋果明令禁止的,沒辦法發揮出動態庫的優勢,實際上動態庫也就沒有存在的必要了
但是,碼農總是喜歡折騰的,一方面骨子里面有一種“你不讓我做我偏要做的倔強”,另一方面用framework確實比用.a加頭文件的方式簡單,所以這一時期的開發者用了很多“奇淫技巧”來制作framework,這就有了Fake Framework 和 Real Framework的區分
在 iOS 8 / iOS6后,iOS平臺添加了動態庫的支持,同時, Xcode 6 也原生自帶了 Framework 支持,注意,前后兩個維度的不同是兩件事,不要混淆。。。
那么,為什么 iOS 8 要添加動態庫的支持?
主要的理由大概就是 Extension 的出現。Extension 和 App 是兩個分開的可執行文件,同時需要共享代碼,這種情況下動態庫的支持就是必不可少的了。但是這種動態 Framework 和系統的 UIKit.Framework 還是有很大區別;還有就是為了支持swift
雖然同樣是動態框架,但是和系統 framework 不同,app 中的使用的 Cocoa Touch Framework 在打包和提交 app 時會被放到 app bundle 中(App 和 Extension 的 Bundle 是共享的),運行在沙盒里,而不是系統中。也就是說,不同的 app 就算使用了同樣的 framework,但還是會有多份的框架被分別簽名,打包和加載,因此蘋果又把這種 Framework 稱為 Embedded Framework,也正是代碼簽名機制,通過AppStore發布的APP是無法通過替換服務端下發framework的方式來進行熱更新!
正文:
今天我們將采取“Aggregate+腳本”制作framework,配置選中手動配置的方式,后面會考慮使用 xcconfig 來默認設置
1, 配置環境,開源頭文件
打開xcode 創建framework項目
必須更改的兩個注意點:
最后你還要更改SDK最低支持的版本,這個和項目更改系統支持的版本一樣,不再贅述
到此基本配置基本完成,也實現了項目的閉源
2, 添加自動打包腳本環境,實現自動打包
常見的錯誤:
1, 沒有改成靜態,不支持動態
2, Defines Module 必須改成NO
3, 支持bitcode的frame操作
如果自己想要制作支持 Bitcode 的 Framework,
1, 工程中開啟 Enable Bitcode
2, Build Setting 中 Other Linker Flags 中加入 -fembed-bitcode。
3, Build Setting 中 Other C Flags 中加入 -fembed-bitcode。
同時支持模擬器真機的配置上文中會用到自動打包的 shell 腳本,我使用的是公司其他大神寫的根據項目需求配置的腳本,這里就不開源了, 大家可以使用以下腳本,也可以實現自動打包
#!/bin/sh
#要build的target名
TARGET_NAME=${PROJECT_NAME}
if [[ $1 ]]
then
TARGET_NAME=$1
fi
UNIVERSAL_OUTPUT_FOLDER="${SRCROOT}/${PROJECT_NAME}/"
#創建輸出目錄,并刪除之前的framework文件
mkdir -p "${UNIVERSAL_OUTPUT_FOLDER}"
rm -rf "${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework"
#分別編譯模擬器和真機的Framework
xcodebuild -target "${TARGET_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${TARGET_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
#拷貝framework到univer目錄
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework" "${UNIVERSAL_OUTPUT_FOLDER}"
#合并framework,輸出最終的framework到build目錄
lipo -create -output "${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework/${TARGET_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/${TARGET_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${TARGET_NAME}.framework/${TARGET_NAME}"
#刪除編譯之后生成的無關的配置文件
dir_path="${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework/"
for file in ls $dir_path
do
if [[ ${file} =~ ".xcconfig" ]]
then
rm -f "${dir_path}/${file}"
fi
done
#判斷build文件夾是否存在,存在則刪除
if [ -d "${SRCROOT}/build" ]
then
rm -rf "${SRCROOT}/build"
fi
rm -rf "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator" "${BUILD_DIR}/${CONFIGURATION}-iphoneos"
#打開合并后的文件夾
open "${UNIVERSAL_OUTPUT_FOLDER}"