系列:iOS開發(fā)-framework的制作與使用
作為一個(gè)iOS開發(fā)人員,無論是出于工作需要,還是出于保密需要...
我們都有可能或多或少的想要將一部分源碼封裝成靜態(tài)或者動(dòng)態(tài)庫,以達(dá)到保密或者版權(quán)的效果
比如公司想要整理框架作為知識(shí)產(chǎn)權(quán)等銷售,比如你做一個(gè)兼職工作,客戶想要項(xiàng)目源碼,涉及隱私你想要保護(hù)起來,比如你想要分享給一個(gè)同時(shí)一項(xiàng)功能,但是不想給源碼...
網(wǎng)上關(guān)于Framework制作的教程數(shù)不勝數(shù),但是大多都是比較老舊的,所以我把這幾天自己制作framework的過程記錄下來,并且使用的是最新的Xcode8環(huán)境。
前段日子我接了一份兼職,項(xiàng)目做完,完整上線,客戶想要源碼,出于保密,我想要把一部分主要的核心代碼封裝起來.
這個(gè)兼職本身并不復(fù)雜,也就幾個(gè)頁面,核心部分是一個(gè)藍(lán)牙的封裝和通訊部分
核心文件也就是這個(gè)藍(lán)牙的實(shí)現(xiàn),其他的話想對(duì)來說就是一些界面和自定義的一些簡(jiǎn)單控件,非核心,我打算封裝一個(gè)framework來替換這部分代碼
步驟:
于是我使用Xcode8創(chuàng)建.
選擇Cocoa Touch Framework->Next
起一個(gè)略屌的名字并創(chuàng)建
把幾個(gè)文件添加進(jìn)入項(xiàng)目
接下來就是設(shè)置一些參數(shù)了
修改項(xiàng)目最低支持版本
修改項(xiàng)目是否支持bitcode
這個(gè)很多人封裝都不一定會(huì)想起來并且設(shè)置,雖然沒有大的影響,但是我覺得我還是保險(xiǎn)起見.
接下來.
這里解釋一下
1.首先是Dead Code Stripping設(shè)置為NO,網(wǎng)上對(duì)此項(xiàng)的解釋如下,大致意思是如果開啟此項(xiàng)就會(huì)對(duì)代碼中的”dead”、”unreachable”的代碼過濾,不過這個(gè)開關(guān)是否關(guān)閉,似乎沒有多大影響,你可以選擇忽視不設(shè)置
2.然后將Link With Standard Libraries關(guān)閉,關(guān)閉連接標(biāo)準(zhǔn)庫,為了避免重復(fù)鏈接,當(dāng)然這里還是可以忽視不設(shè)置
3.最后將Mach-O Type設(shè)為Static Library,framework可以是動(dòng)態(tài)庫也可以是靜態(tài)庫,對(duì)于系統(tǒng)的framework是動(dòng)態(tài)庫,而用戶制作的framework只能是靜態(tài)庫。后面我會(huì)講他們之間的區(qū)別
再繼續(xù)設(shè)置
然后將需要公開的頭文件從Project中拖入Public,至于是否需要將私有的頭文件拖入Private,我覺得直接放在Project中即可,若是Private中有頭文件,打包以后的framework中會(huì)多出一個(gè)Private的文件夾包含著放入Private的頭文件,不過我覺得如果是私有最好還是不要讓別人看到。
然后為了方便用戶使用,修改XLXBLEManager.h的內(nèi)容
這樣初步的環(huán)境都配置好了
接下來我們就要封包了,封包的話我們需要考慮該庫是否需要支持真機(jī)和模擬器同時(shí)進(jìn)行?32位還是64位?debug還是release?
OK,這里是我的設(shè)置
我想要封裝的是release的庫
原因是
Release是發(fā)行版本,比Debug版本有一些優(yōu)化,文件比Debug文件小 Debug是調(diào)試版本,Debug和Release調(diào)用兩個(gè)不同的底層庫。 1、"Debug是調(diào)試版本,包括的程序信息更多" 2、只有DEBUG版的程序才能設(shè)置斷點(diǎn)、單步執(zhí)行、使用TRACE/ASSERT等調(diào)試輸出.3、我封裝成庫了,還需要斷點(diǎn)?單步執(zhí)行?...
選擇release
接下來是支持的版本
Xcode默認(rèn)不支持armv7s 我們可以添加上,當(dāng)然也可以忽略
這里我更改了Build Active Architecture Only設(shè)置我NO,
如果選項(xiàng)設(shè)為YES,其編譯時(shí)模擬器版本只會(huì)生成當(dāng)前模擬器機(jī)器的框架,將其設(shè)置為NO后,發(fā)現(xiàn)用模擬器編譯后生成的framework同時(shí)包含x86_64和i386架構(gòu)。
當(dāng)然這里有幾點(diǎn)要說明,其默認(rèn)的是DEBUG為YES,RELEASE為NO ,我剛剛修改了Scheme為release版本,所以這里其實(shí)默認(rèn)就是NO,我還是可以不用設(shè)置
如果我沒有修改Scheme,仍然是默認(rèn)的debug,那么其就是默認(rèn)的YES,那么結(jié)果是我們選擇iphone5s及以上的模擬器,其框架為x86_64 iPhone5s以下的模擬器,其框架為i386,那樣的話封裝的庫只能使用相對(duì)應(yīng)的模擬器,否則就會(huì)崩潰報(bào)錯(cuò),
為了避免這個(gè)問題,我選擇NO,讓其都支持,無論是debug還是release
OK,所有的準(zhǔn)備工作基本全部做完
接下來是編譯build
首先選擇
我們用commond+B build一下->success
然后再隨便選擇一個(gè)模擬器,使用相同的方式再build一下->success
這樣其實(shí)兩個(gè)framework就生成了
在什么地方呢?
我們能夠看到兩個(gè)文件夾里面分別有一個(gè)framework
上面的是release版本的真機(jī)框架,下面的是release版本的模擬器框架,如果你選擇的scheme是debug的話,則這兩個(gè)文件夾是以debug開頭,
這兩個(gè)framework就是我想要的靜態(tài)庫,我們可以直接添加到想要的項(xiàng)目中使用,當(dāng)然,我們會(huì)想要把兩個(gè)庫合并,讓其能夠在真機(jī)和模擬器同時(shí)運(yùn)行
接下來就會(huì)用到終端命令行
打開終端,輸入
lipo -create
然后把兩個(gè)framework里面的XLXBLEManager分別拖到終端來
這里寫圖片描述
再后面再接著輸入
-output ~/Desktop/framework/XLXBLEManager
完整的命令是這樣的
表示的意思是,合并資源 a b 到指定文件夾 命名為 c 這里~/Desktop/framework/表示桌面的framwork的一個(gè)文件夾,你可以自定義
運(yùn)行,我們?cè)谧烂娴膄ramework文件夾中發(fā)現(xiàn)了合并之后的文件
我們看一下其支持的版本
lipo -info XXX
各種版本都是支持的,是我想要的庫,
我們進(jìn)入剛才的Release-iphoneos文件夾復(fù)制一個(gè)XLXBLEManager.framework到桌面來
使用合成的XLXBLEManager替換掉XLXBLEManager.framework中的XLXBLEManager
這樣,一個(gè)合成之后的完整的.framework就結(jié)束了
接下來是使用framework
在search path 中添加framework的Headers
將之前的代碼引用更換成新的庫的引用
運(yùn)行!
這里封裝的是靜態(tài)的framework,那么動(dòng)態(tài)framework有什么區(qū)別呢?
靜態(tài)庫: 鏈接時(shí)完整地拷貝至可執(zhí)行文件中,被多次使用就有多份冗余拷貝。
動(dòng)態(tài)庫: 鏈接時(shí)不復(fù)制,程序運(yùn)行時(shí)由系統(tǒng)動(dòng)態(tài)加載到內(nèi)存,供程序調(diào)用,系統(tǒng)只加載一次,多個(gè)程序共用,節(jié)省內(nèi)存。[ios暫時(shí)只允許使用系統(tǒng)動(dòng)態(tài)庫];
靜態(tài)庫和動(dòng)態(tài)庫是相對(duì)編譯期和運(yùn)行期的:靜態(tài)庫在程序編譯時(shí)會(huì)被鏈接到目標(biāo)代碼中,程序運(yùn)行時(shí)將不再需要改靜態(tài)庫;而動(dòng)態(tài)庫在程序編譯時(shí)并不會(huì)被鏈接到目標(biāo)代碼中,只是在程序運(yùn)行時(shí)才被載入,因?yàn)樵诔绦蜻\(yùn)行期間還需要?jiǎng)討B(tài)庫的存在。
總結(jié):同一個(gè)靜態(tài)庫在不同程序中使用時(shí),每一個(gè)程序中都得導(dǎo)入一次,打包時(shí)也被打包進(jìn)去,形成一個(gè)程序。而動(dòng)態(tài)庫在不同程序中,打包時(shí)并沒有被打包進(jìn)去,只在程序運(yùn)行使用時(shí),才鏈接載入(如系統(tǒng)的框架如UIKit、Foundation等),所以程序體積會(huì)小很多,但是蘋果不讓使用自己的動(dòng)態(tài)庫,否則審核就無法通過。
這里是我封裝靜態(tài)庫的原因,
如果使用仍然想要使用動(dòng)態(tài)庫的話,那么在使用庫的時(shí)候
還要選擇在Embedded Binaries中添加相應(yīng)的庫
此外
在項(xiàng)目中引入靜態(tài)庫后,archive的時(shí)候如果出現(xiàn)bitcode錯(cuò)誤
bitcode bundle could not be generated because ... was built without full bitcode.All object files and libraries for bitcode must be generated from Xcode Archive or Install build for architecture arm64
所以需要打開庫工程的此選項(xiàng)并加上-fembed-bitcode參數(shù),重新編譯
如此archive時(shí)就不會(huì)出問題了,到此我在制作使用framework時(shí)遇到的問題都在這里了。
此外,還有引用框架編譯的時(shí)候沒有報(bào)錯(cuò),在運(yùn)行的時(shí)候報(bào)錯(cuò)了,是由于鏈接發(fā)生錯(cuò)誤
在使用項(xiàng)目的tagert中的Other Linker Flags 中加入所需的參數(shù),一般是這三個(gè):-ObjC、-all_load、-force_load,嘗試添加了第一個(gè)應(yīng)該就能解決問題了
至此,制作framework的教程就基本結(jié)束了
但是上面的教程是否有點(diǎn)復(fù)雜了?
主要的點(diǎn)就在于制作framwork和合并framework上面,有沒有辦法能夠傻瓜式,一鍵合成需要的靜態(tài)庫或者動(dòng)態(tài)庫呢?肯定有的
在選擇好最低版本,bitcode設(shè)置成NO,Build Active Architecture Only設(shè)置我NO,將Mach-O Type設(shè)為Static Library之后,我們換一種方式
創(chuàng)建一個(gè)target
之后選擇target
添加這段腳本
# Sets the target folders and the final framework product.
# 如果工程名稱和Framework的Target名稱不一樣的話,要自定義FMKNAME
# 例如: FMK_NAME = "MyFramework"
FMK_NAME=${PROJECT_NAME}
# Install dir will be the final output to the framework.
# The following line create it in the root folder of the current project.
INSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework
# Working dir will be deleted after the framework creation.
WRK_DIR=build
DEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.framework
SIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework
# -configuration ${CONFIGURATION}
# Clean and Building both architectures.
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphoneos clean build
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator clean build
# Cleaning the oldest.
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
# Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product.
lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/${FMK_NAME}"
rm -r "${WRK_DIR}"
open "${INSTALL_DIR}"
選擇target之后運(yùn)行
你會(huì)發(fā)現(xiàn)結(jié)果是直接在打包項(xiàng)目中添加了一個(gè)product文件夾并生成了需要的framework文件!
這個(gè)就是我們需要的framework文件,并且是合并之后的文件.
我們可以測(cè)試看看
省去了所有的build->合并->替換的手動(dòng)過程
直接生成需要的framework文件,至于后面怎么使用framework文件,跟上面一樣了...