IOS 多工程依賴(.a)靜態庫和Bundle
前言:當開發到了一定程度後,積累的工具類或者封裝的東西就越來越多,而很多工程會重複使用到這些類,但是又不想每個工程都匯入一遍,可能會想到將這些打包成(.a)庫或者bundle,但是這樣子有變動的時候又得重新編譯一遍,再匯入,耗費不少時間。解決的方法很多,比如建立workspace,多個工程同級使用等。寫這文章,是為了記錄一遍日後翻看。本人的做法如下:
1、靜態庫(.a)庫
在iOS中靜態庫以.a和.framework的形式存在,動態庫以.dylib和.framework的形式存在。之所以.framework既可能是動態庫又可能是靜態庫,是因為蘋果公司禁止使用者級App使用動態庫,而自己卻又堂而皇之的使用動態庫,這就造成了iOS中系統級的.framework是動態庫,使用者級的.framework是靜態庫。二者區別不大,.a是純二進位制檔案,.a檔案不能單獨使用,至少要有.h檔案配合,而.framework除了二進位制檔案外,還包含一些資原始檔(標頭檔案,plist等),由於自身包含了標頭檔案,所以.framework可以單獨使用。
靜態庫建立好了,如果現在編譯的話,就能得到(.a)庫了。
這裡需要注意的是,資料夾“include”裡邊包含的是類的標頭檔案,當你發現添加了依賴,卻找不到某個類的時候,去Build Phases->CopyFiles檢視一下是否有添加了該類的標頭檔案
圖中的第四步是預設的標頭檔案build後存放的位置,可以將它改變(比如:../$(PRODUCT_NAME)/Headers),以便在其它工程中建立依賴時容易找到,這個用處後面會有說到。
而(.a)庫build後的存放位置也是可以改變的,例:
改變前
改變後
之所以改變它們的編譯路徑,就是為了之後的建立依賴方便找到,和改變標頭檔案是一樣的。
需要注意的是,build後的存放分為debug,release,還有模擬器,改變存放路徑是最好分開。
特別需要注意的是,在Build Settings裡邊有個叫Build Active Architecture Only的屬性,為了編譯速度快,Debug情況下預設為Yes,只是編譯當前版本,在這裡我們需要設定成NO,以滿足我們在不同的環境中使用,當碰到靜態庫引用報錯時,我們除了可以檢查路徑是否正確外,還可以利用命令列“lipo -info”來檢視當前引用的庫所支援的環境。
到了這裡,(.a)庫的建立和簡單配置基本完成,根據不同環境編譯後,可以匯入到其它工程使用了。但,這只是簡單使用,顯然不能滿足我們前面說的,現在,接著走。
使用過(.a)庫的人知道,假如我們使用的工具類帶了(.xib)呢?你會發現,編譯的時候,沒有見到xib的檔案
(.xib)檔案沒有關聯上去,怎麼辦呢?
2、Bundle(實際就是一個資料夾)
根據上面說到的可以得出,靜態庫(.a)庫沒有bundle的存在,無法使用(.xib)檔案,根據平時正常工程中使用本地檔案例如(.xib)需要用到NSBundle外,本地的圖片或者(.txt)等也是需要用到,這些檔案在靜態庫中,都需要放到建立的bundle中,這樣,編譯的時候才能編譯到。
因為IOS那類沒有Bundle選擇,故選擇macOS裡邊的,建立後,需要設定一下Build Settings裡邊的Base SDK
然後Bundle的build的路徑也參照前面的設定,將其改變。
設定在編譯主target(.a)庫的前面,先編譯Bundle
然後選中剛才的(.xib),勾上target關聯到Bundle,再編譯
注:如果靜態庫中不需要使用到(.xib)或者圖片等檔案時,Bundle不是必須的。
3、關聯工程
靜態庫和Bundle都建立好,並且配置編譯通過後,接下來就是如何關聯到需要用到的工程中去了
首先,在其它工程我們需要用到(.a)庫,但是我們不想像一般情況那樣子直接將它拖到工程中去,弊端上面有說到。所以需要將主工程與靜態庫建立依賴:
將主工程和靜態庫工程放到同一目錄下(方便路徑關聯)
將編譯後的Bundle用引用的方式匯入主工程(因為Bundle內的檔案不會根據編譯環境變化,比較固定,直接引用)
設定動態依賴關聯靜態庫(Build Settings -> Library Search Paths)
Debug:$(SRCROOT)/../ToolProject/Build/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
Release:$(SRCROOT)/../ToolProject/Build/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
- Other Linker Flags(重點介紹一下)
在使用第三方靜態庫的時候,一般會提醒在Other Linker Flags裡邊配置“-ObjC”或者“-all_load”或者“-force_load”,它們的作用是:
- -ObjC
告訴連結器將庫中的Objective-C類和Category類都載入進來(命名不重複的類),但是這樣子做有個弊端。沒有用到的類也都載入進來了,APP會變大。而且當庫中只存在Category類時,“-ObjC”就不起作用了 - -all_load
強制將所有類都載入進來,能解決只存在Category類的情況。但是,引用多個靜態庫時,它們之中可能存在重新命名的類別,這樣子會有問題 - -force_load
它所做的事情和“-all_load”差不多,但是它能指定完全載入某個靜態庫,而不影響其它的庫。
* - 所以建議“-ObjC”和“-force_load”結合使用
- -ObjC
- 上面說過,靜態庫使用需要結合標頭檔案,所以需要設定搜尋標頭檔案的路徑
$(SRCROOT)/../ToolProject/Build/ToolProject/Headers/
![標頭檔案路徑的設定](https://img-blog.csdn.net/20170726092850996?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcGh5a3k=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
注:以上靜態庫或者標頭檔案路徑的設定都是看實際存放的路徑的,前面一直強調更改它們的路徑就是這個原因。
- 到這裡基本配置就完成了,你會發現在主工程中也可以使用依賴庫中的東西了。
4、指令碼的配置
完成了上面的操作後是可以正常使用了,但是,當你clean了靜態庫後,你會發現,你得重新Build一下,否則無法直接執行主工程,原因,應該大家都懂,那有什麼辦法解決呢
- Build Phase -> New Run Script Phase(要將Run Script放到Compile Sources 的編譯前,原因是編譯主工程前,需先編譯最新的靜態庫)
指令碼程式碼
#!/bin/bash
#進入指令碼目錄
BASEDIR=$(dirname $0)
cd $BASEDIR
build_cmd='xcodebuild OTHER_CFLAGS="-fembed-bitcode" -target ToolProject -configuration '${CONFIGURATION}' -sdk '${SDK_NAME}
echo '執行build命令:'${build_cmd}
${build_cmd}
至此,基本介紹完靜態庫和Bundle的建立和使用。其中的不足和錯漏,懇請斧正。