關(guān)于ios制作靜態(tài)庫,網(wǎng)上篇幅一大堆,這里重點介紹下遇到的坑。
一.如何制作framework
1.創(chuàng)建Framework項目
2.更改Xcode配置
2.1 修改支持版本以及平臺
2.2 修改編譯設置
鏈接類型,Mach-O Type 選擇Static Library(靜態(tài)庫)
2.3 修改 Dead Code Stripping
將Build Settings中Link下面的Dead Code Stripping設置為NO。
注:Dead Code Stripping:舍棄無用代碼,編譯器會對一些沒有引用的無效代碼進行丟棄,這里我們可以設置未NO,盡量保證我們的源代碼的完整性,避免一些額外的錯誤。
2.4.編譯架構(gòu)
2.5 修改Link With Standard Library
將Build Settings中Link下面的Link With Standard Libraries關(guān)閉,我想可能是為了避免重復鏈接
3.修改頭文件的訪問權(quán)限
或者從Build Phases中修改,將需要公開的頭文件拖到Public下面
在Framework的頭文件中導入需要公開的頭文件【這里有坑,下文會講】
4.合并靜態(tài)庫
至此就可以分別選擇模擬器和Any iOS Device進行編譯,得到模擬器下的架構(gòu)和真機下的架構(gòu)。然后通過lipo命令進行合并。下面簡要介紹下相關(guān)lipo命令
// 查看庫信息
lipo -info xxx.framework/xxxxFramework(庫地址)
// 分離架構(gòu)
lipo XXXX.framework/XXXX -thin arm64 -output XXXX.framework/XXXX-arm64
//-output 輸出命令可以簡寫成 -o
//lipo XXXX.framework/XXXX -thin arm64 -o XXXX.framework/XXXX-arm64
// 合并架構(gòu)
lipo -create XXXX.framework/XXXX-armv7 XXXX.framework/XXXX-arm64 -output XXXX.framework/XXXX
// 刪除某個結(jié)構(gòu)
lipo XXXX.framework/XXXX -remove i386 -output XXXX.framework/XXXX
5.添加生成Framework的腳本
鑒于以上手動合并庫的形式較為low,可以創(chuàng)建一個腳本自動輸出合并之后的庫
#!/bin/sh
#要build的target名
TARGET_NAME=${PROJECT_NAME}
if [[ $1 ]]
then
TARGET_NAME=$1
fi
UNIVERSAL_OUTPUT_FOLDER="${SRCROOT}/${PROJECT_NAME}/"
#創(chuàng)建輸出目錄,并刪除之前的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}"
lipo "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/${TARGET_NAME}" -remove arm64 -output "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/${TARGET_NAME}"
#合并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}"
#刪除編譯之后生成的無關(guān)的配置文件
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}"
選擇Shell target,直接編譯則生成Debug模式的Framework,Archive則生成Release模式的Framework
編譯成功后將自動打開Framework所在的目錄
二.避坑指南
坑1:公開的頭文件import的時候使用雙引號,外部使用的時候報找不到頭文件
解決思路:一定要用尖括號的形式引用。因為打包成靜態(tài)庫之后,文件會打包在靜態(tài)庫資源包里,如果通過雙引號引用導入,在靜態(tài)庫的項目里不會報錯,但是外部使用進行編譯的時候會從主項目中找頭文件,那么肯定會報找不到頭文件的error。
//一定要用#import <xxxx/xxxx.h> 引導編譯器本模塊尋找頭文件
#import <aaaa/aaaa.h>
#import <bbbb/bbbb.h>
#import <cccc/cccc.h>
坑2:所有暴露為public的頭文件中,存在部分頭未暴露的文件引用。
比如aaaa.h設為public了,aaaa.h中引用了dddd.h,但是dddd.h并沒有暴露出來,靜態(tài)庫內(nèi)部編譯沒問題,但是外部使用會報sdk編譯錯誤。
解決思路:暴露出來的頭文件中,如有相關(guān)聯(lián)的頭文件也要一定暴露。或者將原本在.h文件中的引用放入.m文件中。
坑3:靜態(tài)庫中包含category,外部使用crash,報方法找不到。
解決方案(以下2點都要設置):
1.靜態(tài)庫中存在category時,在 Framework 文件中添加 target --> Build setttings --> linking --> Other linker flags 添加 -Objc,同時支持-all_load,保證分類能打包進去。
2.外部使用的時候,也需要在 target --> Build setttings --> linking --> Other linker flags 添加 -Objc,同時支持-all_load。
image.png
這里關(guān)于other Link Flags中的-ObjC,-all_load,-force_load,這里有詳細說明:http://www.lxweimin.com/p/f7b0aa817cff
坑4:庫中有第三方庫的引用,外部使用時會報存在重復的.o文件
解決方案:
方案A:通過rename修改類名,變成自己的私有框架
在打包成framework之前,在每個第三方框架的類名的前面,通過Xcode的rename操作對類名、系統(tǒng)分類的方法名,枚舉,結(jié)構(gòu)體進行統(tǒng)一修改,比如加一個前綴(隨便你加什么)。 這是個細活,但是一勞永逸,這個框架以后就是屬于你自己的了,隨便你怎么去修改。跟其他工程中的類,也不會有任何沖突。
image.png
優(yōu)點:一勞永逸,不會對外界產(chǎn)生影響。
缺點:會增加包的體積,同時rename是個細致活,尤其是對系統(tǒng)的一些分類的方法名、枚舉值進行組個rename,比較麻煩。
方案B:制作靜態(tài)庫時對三方庫只是添加單純添加引用,不導入到模塊中來。外部項目使用該的時候,由外部對該三方庫進行手動導入或者pod引入。
優(yōu)點:包體積小,不存在代碼相同功能代碼造成的代碼冗余。
缺點:需要外部使用的時候添加三方庫的依賴,中間可能會存在一些報錯,接入成本較高。
參考:
http://www.lxweimin.com/p/a80ce7d25ddf
http://www.lxweimin.com/p/f7b0aa817cff
http://www.lxweimin.com/p/e5726852bb82
http://www.lxweimin.com/p/b92912216d65
http://www.lxweimin.com/p/d79f6c866fdb