Xcode10制作 framework詳細(xì)步驟及坑說(shuō)明

在開(kāi)發(fā)中,我們經(jīng)常使用使用封裝的庫(kù)進(jìn)行開(kāi)發(fā),比如微信、微博分享等sdk,很容易實(shí)現(xiàn)我們的登錄分享功能。

一、什么是庫(kù)?

庫(kù)是源代碼經(jīng)過(guò)編譯,形成的二進(jìn)制代碼,別人項(xiàng)目中使用我們的庫(kù)的時(shí)候,庫(kù)在參與編譯的時(shí)候,直接鏈接,按照鏈接的方式,可以把庫(kù)分為靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)。

二、靜態(tài)庫(kù)與動(dòng)態(tài)庫(kù)的區(qū)別

靜態(tài)庫(kù):鏈接時(shí)完整地拷貝至可執(zhí)行文件中,被多次使用就有多份冗余拷貝。
動(dòng)態(tài)庫(kù):鏈接時(shí)不復(fù)制,程序運(yùn)行時(shí)由系統(tǒng)動(dòng)態(tài)加載到內(nèi)存,供程序調(diào)用,系統(tǒng)只加載一次,多個(gè)程序共用,節(jié)省內(nèi)存。(蘋果不允許用戶打包.tbd .dylib的動(dòng)態(tài)庫(kù))

三、庫(kù)的表現(xiàn)形式

靜態(tài)庫(kù): .a. 與.framework
動(dòng)態(tài)庫(kù): .dylib.framework,如UIKit.framework.

四、靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的區(qū)別

.a是一個(gè)純二進(jìn)制文件,.framework是一個(gè)文件夾,除了可執(zhí)行文件外,還有資源文件。

.framework內(nèi)部文件

注意.a文件不能直接使用,至少需要.h文件配合,而.framework可以直接使用。

五、靜態(tài)庫(kù)的制作

NOTE:蘋果不允許用戶打包 .tbd .dylib的動(dòng)態(tài)庫(kù)。

1.打開(kāi)Xcode,創(chuàng)建framework。


2.指定項(xiàng)目發(fā)布系統(tǒng)信息。



3.創(chuàng)建你需要的文件。把你需要的頭文件公開(kāi)。



注意項(xiàng)目中使用的資源文件,需要單獨(dú)創(chuàng)建一個(gè)文件夾如source.bundle中,訪問(wèn)的時(shí)候,
//因?yàn)楹竺鏁?huì)把資源放到source.bundle里
//[UIImage imageNamed:@"dog.png"],就算圖片在Assets.xcassets里,圖片名要帶后綴例如".png",因?yàn)橐袮ssets.xcassets里的圖片都移到bundle里。
[UIImage imageNamed:@"source.bundle/dog.png"] 
// [[NSBundle mainBundle] loadNibNamed:@"LYFShowView" owner:self options:nil].firstObject 錯(cuò)誤的訪問(wèn)方式
 [[NSBundle mainBundle] loadNibNamed:@"source.bundle/myNibView" owner:self options:nil].firstObject

4.配置庫(kù)。如輸出方式。以及架構(gòu)方式。

指定debug和release都創(chuàng)建對(duì)應(yīng)的架構(gòu)

支持的架構(gòu)如上圖vaild Architecture 配置。

配置輸出類型為靜態(tài)庫(kù)

注意:Dead Code Stripping 以及 Link With Standard Libaries 默認(rèn)yes,可以不配置為NO
5.生成庫(kù)。
1>手動(dòng)生成
2>腳本生成。

下面分別說(shuō)明2種方式:

1>手動(dòng)生成
1.配置對(duì)應(yīng)的運(yùn)行環(huán)境,默認(rèn)Debug.


配置對(duì)應(yīng)的運(yùn)行環(huán)境

模擬器編譯生成對(duì)應(yīng)的framework,然后依次真機(jī)編譯生成對(duì)應(yīng)的framework。選中Products 目錄下的framework,show in finder,如下圖。



然后打開(kāi)終端,使用命令 lipo -create 模擬器文件 真機(jī)二進(jìn)制文件 -output /xxx/test
lipo -create /Users/iOS/Library/Developer/Xcode/DerivedData/18.11.13-testFramework-asdmjgqiyqnbpwcvmrfohdcnxzlm/Build/Products/Debug-iphoneos/D_18_11_13_testFramework.framework/D_18_11_13_testFramework 
/Users/iOS/Library/Developer/Xcode/DerivedData/18.11.13-testFramework-asdmjgqiyqnbpwcvmrfohdcnxzlm/Build/Products/Debug-iphonesimulator/D_18_11_13_testFramework.framework/D_18_11_13_testFramework 
-output /Users/iOS/Desktop/D_18_11_13_testFramework

合成對(duì)應(yīng)的包含模擬器和真機(jī)的文件。然后將該文件拖拽到真機(jī)或者模擬器的framework中替換對(duì)應(yīng)的二進(jìn)制文件即可。
可以通過(guò)lipo -info xxx查看對(duì)應(yīng)的文件架構(gòu)。

//終端輸入指令,可以看出我們成功合成了。
lipo -info /Users/iOS/Desktop/D_18_11_13_testFramework 
Architectures in the fat file: /Users/zhaobin/Desktop/D_18_11_13_testFramework are: armv7 i386 x86_64 arm64 

2>腳本生成。



創(chuàng)建新的Targets Aggregate,然后再添加新的腳本。


選擇New Run Script phase

關(guān)于此次腳本問(wèn)題,Xcode 9之前的腳本已經(jīng)不能使用。那是因?yàn)樯赡_本的時(shí)候是在項(xiàng)目文件目錄進(jìn)行操作, xcodebulid clean 指令刪除了當(dāng)前的編譯文件。
FMK_NAME=${PROJECT_NAME}
INSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework
WRK_DIR=build

DEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.framework
SIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework

xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphoneos clean build
#Xcode10此次clean會(huì)把當(dāng)前目錄刪除然后,重新編譯生成新的(即上一步真機(jī)生成)。
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator clean build

if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"

lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/${FMK_NAME}"
rm -r "${WRK_DIR}"
open "${SRCROOT}/Products/"

簡(jiǎn)單的方法,使用腳本就是將xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator clean build中的clean去掉。
保證模擬器和真機(jī)生成的文件都存在。
NOTE:此種方式不保證一定可以合成,建議用最新的

還有一種新的腳本,本質(zhì)就是直接到app的沙盒build目錄去copy,然后生成新的文件,放到項(xiàng)目目錄。

#!/bin/sh
#要build的target名(若一個(gè)工程有多個(gè)target,最好手動(dòng)指定需要打包的目標(biāo),如TARGET_NAME="framework名")
TARGET_NAME=${PROJECT_NAME}
if [[ $1 ]]
then
TARGET_NAME=$1
fi
UNIVERSAL_OUTPUT_FOLDER="${SRCROOT}/Products/"

#創(chuàng)建輸出目錄,并刪除之前的framework文件
mkdir -p "${UNIVERSAL_OUTPUT_FOLDER}"
rm -rf "${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework"

#分別編譯模擬器和真機(jī)的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}"

#刪除編譯之后生成的無(wú)關(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"

#打開(kāi)合并后的文件夾
open "${UNIVERSAL_OUTPUT_FOLDER}"

感興趣的朋友可以自己試試看~~~(簡(jiǎn)單注釋掉rm指令即可,#開(kāi)頭)
shell參考文檔

六、生成庫(kù)中的注意點(diǎn)

1.制作framework,注意項(xiàng)目名稱不能包含”.”。如test.dog 項(xiàng)目名,生成的庫(kù)是test_dog.framework,導(dǎo)致腳本運(yùn)行失敗。(手動(dòng)生成也是可以的,但遵循命名規(guī)則還是好的)
項(xiàng)目的名字也不要以數(shù)字開(kāi)頭,如”2018_11_13_testFramework”,生成018_11_13_testFramework.framework.導(dǎo)致生成失敗。
2.路徑縮寫區(qū)分大小寫,如(SRCROOT)表示當(dāng)前根路徑,不能表示(srcroot)
3.關(guān)于腳本的報(bào)錯(cuò)。如打開(kāi)一個(gè)目錄不存在,或者copy一個(gè)文件不存在,會(huì)報(bào)錯(cuò).

cp: build/Release-iphoneos/D_18_11_13_testFramework.framework——/: No such file or directory
fatal error: lipo: can't open input file: build/Release-iphonesimulator/D_18_11_13_testFramework.framework/D_18_11_13_testFramework (No such file or directory)
The file /Products does not exist.
Command PhaseScriptExecution failed with a nonzero exit code

到了這萬(wàn)事ok(關(guān)于如何用就不多說(shuō)了)。。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容