iOS 庫(kù).a 和 framework的區(qū)別和創(chuàng)建

一直未間斷SDK的工作,總是在做到現(xiàn)在從未總結(jié),現(xiàn)在總結(jié)一下,備錄一下,供大家參考和借鑒。

一、什么是庫(kù)?

共享代碼便是庫(kù),實(shí)現(xiàn)代碼的復(fù)用,一般分為靜態(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)加在到內(nèi)存中,供系統(tǒng)調(diào)用,系統(tǒng)加在一次,多次使用,共用節(jié)省內(nèi)存。

三、iOS的靜態(tài)庫(kù)?

.a和.framework 樣式

四、iOS的動(dòng)態(tài)庫(kù)?

.dylib和.framework

五、為什么framework既是靜態(tài)又是動(dòng)態(tài)?

系統(tǒng)的framework是動(dòng)態(tài)的,我們自己創(chuàng)建的是靜態(tài)的。

六、.a 和 .framework 的區(qū)別是什么?

.a 是單純的二進(jìn)制文件,.framework是二進(jìn)制問價(jià)+資源文件。
其中.a 不能直接使用,需要 .h文件配合,而.framework則可以直接使用。
.framework = .a + .h + sorrceFile(資源文件)

七、為什么使用靜態(tài)庫(kù)?

共享代碼,方便使用。
實(shí)現(xiàn)代碼的模塊化,固定的業(yè)務(wù)模塊話,減少開發(fā)的重復(fù)勞動(dòng)。
和別人分享代碼,但又不想讓別人知道代碼的具體實(shí)現(xiàn)。

八、實(shí)現(xiàn)靜態(tài)庫(kù)的注意事項(xiàng):


1 、注意理解:無論是.a靜態(tài)庫(kù)還.framework靜態(tài)庫(kù),我們需要的都是二進(jìn)制文件+.h+資源文件,不同的是,.a本身只是二進(jìn)制文件,需要配上.h和資源文件才能使用,而.framework本身已經(jīng)包含了二進(jìn)制文件、.h和資源文件,可以直接使用。
2 、圖片資源的處理:兩種靜態(tài)庫(kù),一般都是把圖片文件單獨(dú)的放在一個(gè).bundle文件中,一般.bundle的名字和.a或.framework的名字相同。新建一個(gè)文件夾,把它改名為.bundle,右鍵->顯示包內(nèi)容,之后就可以向其中添加資源文件。
3 、把category打成靜態(tài)庫(kù),但是在使用靜態(tài)庫(kù)的工程中,調(diào)用category中的方法時(shí)會(huì)有找不到該方法的運(yùn)行時(shí)錯(cuò)誤(selector not recognized),解決辦法是:在使用靜態(tài)庫(kù)的工程中配置other linker flags的值為-ObjC。
4 如果一個(gè)靜態(tài)庫(kù)很復(fù)雜,需要暴露的.h比較多的話,就可以在靜態(tài)庫(kù)的內(nèi)部創(chuàng)建一個(gè).h文件(一般這個(gè).h文件的名字和靜態(tài)庫(kù)的名字相同),然后把所有需要暴露出來的.h文件都集中放在這個(gè).h文件中,而那些原本需要暴露的.h都不需要再暴露了,只需要把.h暴露出來就可以了。


封裝framework

1、打開xcode,新建工程


image.png

2、創(chuàng)建功能類


image.png

3、實(shí)現(xiàn)功能類
.h文件


image.png

.m文件


image.png

4、Xcode項(xiàng)目配置
將framework設(shè)置成靜態(tài)庫(kù)
image.png

5、設(shè)置header,將需要暴露的頭文件放在public下面,隱藏在project或者private下面無法被引用。


image.png

然后需要在mySDk.h(必須是公開的,否則無法引用)中將你所有要公開的.h引入。
image.png

修改下面:如果是YES,說明當(dāng)前活躍的版本是8,如果只編譯此機(jī)型,就設(shè)置成YES,適配所有的架構(gòu)Architecture 設(shè)置為No。
image.png

打包

手動(dòng)打包
1、選中模擬器,command+B
2、選中真機(jī),command+B
3、在finder中找到framework文件


image.png

會(huì)發(fā)現(xiàn),真機(jī)和模擬器的包


image.png

4、通過終端命令將兩個(gè)framework合為一個(gè)模擬器和真機(jī)都可使用的framework。
打開終端,輸入lipo -create命令,將
Debug-iphoneos下mySdk.framework目錄下的mySDk.framework文件
image.png

拖拽到終端中,會(huì)自動(dòng)有空格。然后將Debug-iphonesimulator下mySDk.framework目錄下的mySDk.framework文件
image.png

拖拽進(jìn)來,也會(huì)自動(dòng)有空格,然后輸入 -output,敲空格,在引入一個(gè)新的路徑。最后敲回車,這樣就合并了。


image.png

將生成的文件拖回上面的一個(gè)mySDk.framework的文件夾中,就生成我們最終的framework。
5、使用
將framework拖入新的項(xiàng)目
image.png

將framework添加到
image.png

假如還是不可以,設(shè)置framework和.h的搜索路徑
image.png
image.png
image.png

第二種方法

1、創(chuàng)建.a 靜態(tài)庫(kù)


image.png

2、.h文件


image.png

.m文件
image.png

3.添加腳本生成

set -e

if [ ${DEPLOYMENT_LOCATION} == "YES" ]; then
echo "Deploying, exit"
exit 0
fi

export FRAMEWORK_LOCN="${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework"
rm -rf ${FRAMEWORK_LOCN}
# Create the path to the real Headers die
mkdir -p "${FRAMEWORK_LOCN}/Versions/A/Headers"

# Create the required symlinks
/bin/ln -sfh A "${FRAMEWORK_LOCN}/Versions/Current"
/bin/ln -sfh Versions/Current/Headers "${FRAMEWORK_LOCN}/Headers"
/bin/ln -sfh "Versions/Current/${PRODUCT_NAME}" \
"${FRAMEWORK_LOCN}/${PRODUCT_NAME}"

echo "taget built dir=${TARGET_BUILD_DIR},public headers path=${PUBLIC_HEADERS_FOLDER_PATH}"
# Copy the public headers into the framework
/bin/cp -a "${TARGET_BUILD_DIR}/${PUBLIC_HEADERS_FOLDER_PATH}/" \
"${FRAMEWORK_LOCN}/Versions/A/Headers"

echo "Framework built successfully"

4、xcode 配置


image.png

1、選中TARGETS下的工程,點(diǎn)擊上方的Editor,選擇Add Target創(chuàng)建一個(gè)Aggregate.


image.png

2、嵌入腳本。選中剛剛創(chuàng)建的Aggregate,然后選中右側(cè)的Build Phases,點(diǎn)擊左下方加號(hào),選擇New Run Script Phase


image.png

3、腳本是:
set -e

# If we're already inside this script then die
if [ -n "$RW_MULTIPLATFORM_BUILD_IN_PROGRESS" ]; then
exit 0
fi
export RW_MULTIPLATFORM_BUILD_IN_PROGRESS=1

RW_FRAMEWORK_NAME=${PROJECT_NAME}
RW_INPUT_STATIC_LIB="lib${PROJECT_NAME}.a"
RW_FRAMEWORK_LOCATION="${BUILT_PRODUCTS_DIR}/${RW_FRAMEWORK_NAME}.framework"
#RW_BUNDLE_NAME="VKSdkResources"


function build_static_library {
    # Will rebuild the static library as specified
    #     build_static_library sdk
    xcrun xcodebuild -project "${PROJECT_FILE_PATH}" \
    -target "${TARGET_NAME}" \
    -configuration "${CONFIGURATION}" \
    -sdk "${1}" \
    ONLY_ACTIVE_ARCH=NO \
    BUILD_DIR="${BUILD_DIR}" \
    OBJROOT="${OBJROOT}" \
    BUILD_ROOT="${BUILD_ROOT}" \
    SYMROOT="${SYMROOT}" $ACTION
}

function make_fat_library {
    # Will smash 2 static libs together
    #     make_fat_library in1 in2 out
    #xcrun
    lipo -create "${1}" "${2}" -output "${3}"
}


# 1 - Extract the platform (iphoneos/iphonesimulator) from the SDK name
if [[ "$SDK_NAME" =~ ([A-Za-z]+) ]]; then
RW_SDK_PLATFORM=${BASH_REMATCH[1]}
else
echo "Could not find platform name from SDK_NAME: $SDK_NAME"
exit 1
fi

# 2 - Extract the version from the SDK
if [[ "$SDK_NAME" =~ ([0-9]+.*$) ]]; then
RW_SDK_VERSION=${BASH_REMATCH[1]}
else
echo "Could not find sdk version from SDK_NAME: $SDK_NAME"
exit 1
fi

# 3 - Determine the other platform
if [ "$RW_SDK_PLATFORM" == "iphoneos" ]; then
RW_OTHER_PLATFORM=iphonesimulator
else
RW_OTHER_PLATFORM=iphoneos
fi

# 4 - Find the build directory
if [[ "$BUILT_PRODUCTS_DIR" =~ (.*)$RW_SDK_PLATFORM$ ]]; then
RW_OTHER_BUILT_PRODUCTS_DIR="${BASH_REMATCH[1]}${RW_OTHER_PLATFORM}"
else
echo "Could not find other platform build directory."
exit 1
fi

# Build the other platform.
build_static_library "${RW_OTHER_PLATFORM}${RW_SDK_VERSION}"

# If we're currently building for iphonesimulator, then need to rebuild
#   to ensure that we get both i386 and x86_64
if [ "$RW_SDK_PLATFORM" == "iphonesimulator" ]; then
build_static_library "${SDK_NAME}"
fi

# Join the 2 static libs into 1 and push into the .framework
make_fat_library "${BUILT_PRODUCTS_DIR}/${RW_INPUT_STATIC_LIB}" \
"${RW_OTHER_BUILT_PRODUCTS_DIR}/${RW_INPUT_STATIC_LIB}" \
"${RW_FRAMEWORK_LOCATION}/Versions/A/${RW_FRAMEWORK_NAME}"

# Ensure that the framework is present in both platform's build directories
cp -a "${RW_FRAMEWORK_LOCATION}/Versions/A/${RW_FRAMEWORK_NAME}" \
"${RW_OTHER_BUILT_PRODUCTS_DIR}/${RW_FRAMEWORK_NAME}.framework/Versions/A/${RW_FRAMEWORK_NAME}"

# Copy the framework to the user's desktop
ditto "${RW_FRAMEWORK_LOCATION}" "${SRCROOT}/${RW_FRAMEWORK_NAME}.framework"

open ${SRCROOT}

4、運(yùn)行


image.png

將會(huì)生成framework在本文件中,并會(huì)自動(dòng)打開文件夾
5、測(cè)試


image.png

運(yùn)行之前需要查看framework是否已經(jīng)拖入
image.png

總結(jié):


1、.h文件的一定要包含自己想要暴露的。
2、開始打包的時(shí)候,一定要在選中模擬器和選中真機(jī)上邊分別編譯一次,選用的我第二的方法,可以直接運(yùn)行framework。
3、調(diào)用的時(shí)候分清楚是類方法還是實(shí)例方法。
4、在制作framework或者lib的時(shí)候,如果使用了category,則使用改FMWK的程序運(yùn)行時(shí)會(huì)crash,此時(shí)需要在該工程中 other linker flags添加兩個(gè)參數(shù) -ObjC -all_load。(這點(diǎn)沒有親測(cè))
5、帶有資源文件的需要把圖片打包成Bundle文件,和framework一起拷貝到相應(yīng)的項(xiàng)目中。
6、公開的類中如果引用的private的類,打包以后對(duì)外會(huì)報(bào)錯(cuò),找不到那個(gè)private的類,可以把那個(gè)private的.h放到(也沒親測(cè))
7、namespace 沖突。靜態(tài)庫(kù)用了某第三方庫(kù),項(xiàng)目也用了同樣的第三方庫(kù),在編譯的時(shí)候就會(huì)有 duplicate symbol 錯(cuò)誤,因?yàn)橛袃煞萃瑯拥牡谌綆?kù)。解決辦法就是把用到的第三方庫(kù)加上自定義前綴,包括類名、delegate 協(xié)議、常量名,尤其需要注意 Category 的方法名要修改。
8、封裝靜態(tài)庫(kù)的時(shí)候應(yīng)盡量避免引入重量級(jí)第三方庫(kù)。
9、一個(gè)靜態(tài)庫(kù)要有自己獨(dú)有的前綴,所有類名、常量等都要加同樣的前綴。
10、
真機(jī)+模擬器支持。Xcode 默認(rèn)只會(huì)用當(dāng)前環(huán)境(真機(jī)或模擬器)生成靜態(tài)庫(kù),這樣的 SDK 不方便其他項(xiàng)目開發(fā)時(shí)調(diào)試。解決辦法就是通過腳本生成一份通用庫(kù),build_universal_library.sh,via SO.
11、圖片等資源文件用
bundle 方式打包。一個(gè)簡(jiǎn)單制作 bundle 的方法:新建文件夾,重命名為 YourSDK.bundle,然后 Show Package Contents 打開,加入圖片。使用圖片的時(shí)候需要指明 bundle: [UIImage imageNamed:@"YourSDK.bundle/img.png"]。也可以用 Target 方式制作 bundle,比如 iOS Library With Resourceshttp://www.galloway.me.uk/tutorials/ios-library-with-resources/.
12、如果 SDK 有用到
Category**,注意項(xiàng)目設(shè)置 Other Linker Flags 添加 -ObjC。(后邊介紹了-ObjC的作用)


注意:

編譯過程:
從C代碼到可執(zhí)行文件經(jīng)歷的步驟是:源代碼 > 預(yù)處理器 > 編譯器 > 匯編器 > 機(jī)器碼 > 鏈接器 > 可執(zhí)行文件

在最后一步需要把.o文件和C語言運(yùn)行庫(kù)鏈接起來,這時(shí)候需要用到ld命令。源文件經(jīng)過一系列處理以后,會(huì)生成對(duì)應(yīng)的.obj文件,然后一個(gè)項(xiàng)目必然會(huì)有許多.obj文件,并且這些文件之間會(huì)有各種各樣的聯(lián)系,例如函數(shù)調(diào)用。鏈接器做的事就是把這些目標(biāo)文件和所用的一些庫(kù)鏈接在一起形成一個(gè)完整的可執(zhí)行文件。Other linker flags設(shè)置的值實(shí)際上就是ld命令執(zhí)行時(shí)后面所加的參數(shù)

下面逐個(gè)介紹3個(gè)常用參數(shù):
-ObjC:加了這個(gè)參數(shù)后,鏈接器就會(huì)把靜態(tài)庫(kù)中所有的Objective-C類和分類都加載到最后的可執(zhí)行文件中
-all_load:會(huì)讓鏈接器把所有找到的目標(biāo)文件都加載到可執(zhí)行文件中,但是千萬不要隨便使用這個(gè)參數(shù)!假如你使用了不止一個(gè)靜態(tài)庫(kù)文件,然后又使用了這個(gè)參數(shù),那么你很有可能會(huì)遇到ld: duplicate symbol錯(cuò)誤,因?yàn)椴煌膸?kù)文件里面可能會(huì)有相同的目標(biāo)文件,所以建議在遇到-ObjC失效的情況下使用-force_load參數(shù)。
-force_load:所做的事情跟-all_load其實(shí)是一樣的,但是-force_load需要指定要進(jìn)行全部加載的庫(kù)文件的路徑,這樣的話,你就只是完全加載了一個(gè)庫(kù)文件,不影響其余庫(kù)文件的按需加載。

demo :mySDK
demo :myTool
希望大家批評(píng)指正。

持續(xù)更新:

1、關(guān)于使用static library的這部分,我們創(chuàng)建的不管是動(dòng)態(tài)庫(kù)還是靜態(tài)庫(kù)在工程中的呈現(xiàn)都是靜態(tài)的,因?yàn)橄到y(tǒng)不允許我們使用動(dòng)態(tài)的庫(kù),我們?yōu)榱藴p少不必要的麻煩直接選擇靜態(tài)的,假如我記得沒有錯(cuò),你選擇動(dòng)態(tài)的在提交之后應(yīng)該會(huì)被拒。
2、關(guān)于上面的使用公共的第三方庫(kù),例如AFNetWorking、SDWebImage等這個(gè)第三方庫(kù)的問題,我在上面寫到過,我們可以將所有的名字、協(xié)議修改,但是工作量有點(diǎn)巨大,而且容易出錯(cuò),那我們就可以將這些公用的庫(kù)放在外面,打成SDK之后讓別人引用sdk的時(shí)候去引入這些第三方框架,這是可以的,我已經(jīng)做過實(shí)踐了。

參考:
iOS封裝功能生成 .framework

最后編輯于
?著作權(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)容