先來(lái)看幾個(gè)概念定義:
- 什么是庫(kù)?
庫(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)存 - iOS里靜態(tài)庫(kù)的形式练般?
.a 和 .framework - iOS動(dòng)態(tài)庫(kù)的形式?
.dylib 和 .framework - framework為什么既是靜態(tài)庫(kù)有事動(dòng)態(tài)庫(kù)锈候?
系統(tǒng)的.framework是動(dòng)態(tài)庫(kù)踢俄,我們自己建立的.framework是靜態(tài)庫(kù) - .a 和 .framework有什么區(qū)別?
.a 是一個(gè)純二進(jìn)制文件,.framework中除了有二進(jìn)制文件之外還有資源文件
.a 文件不能直接使用晴及,至少要有.h文件配合都办,.framework文件可以直接使用
.a + .h +sourceFile = .framework - 為什么使用靜態(tài)庫(kù)?
方便共享代碼虑稼,便于合理使用琳钉。(懶逼格高)
實(shí)現(xiàn)iOS程序的模塊化≈刖耄可以把固定的業(yè)務(wù)模塊化成靜態(tài)庫(kù)歌懒。(懶高大上)
和別人分享你的代碼庫(kù),但不想讓別人看到你代碼的實(shí)現(xiàn)溯壶。(怕被噴)
開(kāi)發(fā)第三方SDK的需要及皂。
我們可以看出.a的封裝和.framework的封裝差不多,也有模擬器和真機(jī)合并的過(guò)程且改,通過(guò)上邊的圖片我們可以看出.a 和.framework的區(qū)別验烧,就是.a+.h+soureFile=.framework∮瞩耍可以看出我們直接封裝.framework其實(shí)是最好的碍拆。不啰嗦了,先來(lái)看看.a文件是怎么封裝的
一 .a文件的封裝
(一)創(chuàng)建一新工程TestSDK,選擇Framework & Library中的 Cocoa Touch Static Library感混,如圖1
只留下.h文件就可以。
(二)創(chuàng)建你要封裝的模塊弧满,由于本文主要是講述制作流程這里我直接拖入一個(gè)模塊如圖2:
在TestSDK.h中引入你的控件的頭文件ChannelAlert.h即可洽蛀。
(三)分別選擇模擬器和真機(jī)編譯工程,在Products下會(huì)生成.a文件疟赊,show in finder會(huì)發(fā)現(xiàn)如圖3兩個(gè).a文件
iphoneos 適用于真機(jī),iphonesimulator適用于模擬器峡碉。打開(kāi)終端合并兩個(gè)文件成為通用版本近哟,命令
lipo -create
模擬器.a文件目錄
真機(jī).a文件目錄
-output 輸出目錄/文件
如圖4:(嘿嘿是不是很紅)
看到libTestSDK.a了吧,這就是通用版本的.a文件
(四)使用方法:
將此文件夾拖入到你需要使用的工程中地来,在需要的地方導(dǎo)入頭文件TestSDK.h如圖6:
功能按照你正常的使用方式就可以了戳玫。看看我的效果
到此簡(jiǎn)單的.a文件制作完成了未斑,下面我們來(lái)看看更好的framework是如何制作的
二 .framework文件的封裝
(一)創(chuàng)建工程TestFramework,如圖F1:
(二)創(chuàng)建自己的類(lèi)咕宿,Tool.h,Tool.m,如圖F2:
如果有依賴庫(kù)就要添加依賴庫(kù)(本文重點(diǎn)是流程,這里就不多加贅述)蜡秽。在TestFramework.h中#import "Tool.h"府阀。
(三)在Build Phases->Headers中設(shè)置你要暴露的接口,主要設(shè)置Public和Private芽突,這里我把Tool.h移動(dòng)到了Public中如圖:
選擇模擬器和真機(jī)试浙,分別編Command+B一次,
iphoneos 對(duì)應(yīng)的是真機(jī)寞蚌,iphonesimulator對(duì)應(yīng)的模擬器
下面我們將兩個(gè)framework合并田巴,要用到腳本語(yǔ)言
if [ "${ACTION}" = "build" ]
then
INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}.framework
DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework
SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
#ditto "${DEVICE_DIR}/Headers" "${INSTALL_DIR}/Headers"
lipo -create "${DEVICE_DIR}/${PROJECT_NAME}" "${SIMULATOR_DIR}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}"
#open "${DEVICE_DIR}"
open "${SRCROOT}/Products"
fi
如圖:在4位置添加上面的腳本,然后Run(xcode左上角的黑三角)之后會(huì)彈框在Products里面就可以看到TestFramework.framework了挟秤。
(四)使用方法
在你需要的地方導(dǎo)入頭文件:如圖
至此,簡(jiǎn)單的.framework就算是制作完成了。這也僅是一個(gè)流程啄糙,畢竟framework博大精深笛臣。寫(xiě)一點(diǎn)注意事項(xiàng)吧
1、.h文件的外漏一定要保證是自己的想要外漏的隧饼。不想外漏的就別外漏了沈堡。
2、開(kāi)始打包的時(shí)候燕雁,一定要在選中模擬器和選中真機(jī)上邊分別編譯一次诞丽, 我覺(jué)得之前在家里沒(méi)有真機(jī)的時(shí)候編譯的好像不對(duì)。
3拐格、終端也可以合并(這里就不寫(xiě)了僧免,有興趣的可以自己搜一搜),在終端上邊合并的時(shí)候可能是error并生成一個(gè).lipo文件捏浊,不要怕懂衩,大膽修改成同名的不掛后綴的同名文件。
4金踪、調(diào)用的時(shí)候分清楚是類(lèi)方法還是實(shí)例方法浊洞,方便調(diào)用。
5胡岔、在制作framework或者lib的時(shí)候法希,如果使用了category,則使用改FMWK的程序運(yùn)行時(shí)會(huì)crash靶瘸,此時(shí)需要在該工程中 other linker flags添加兩個(gè)參數(shù) -ObjC -all_load苫亦。(這點(diǎn)沒(méi)有親測(cè))
6、帶有圖片資源的需要把圖片打包成Bundle文件怨咪,和framework一起拷貝到相應(yīng)的項(xiàng)目中著觉。
7、公開(kāi)的類(lèi)中如果引用的private的類(lèi)惊暴,打包以后對(duì)外會(huì)報(bào)錯(cuò)饼丘,找不到那個(gè)private的類(lèi),可以把那個(gè)private的.h放到(也沒(méi)親測(cè))
8辽话、**namespace 沖突肄鸽。**靜態(tài)庫(kù)用了某第三方庫(kù),項(xiàng)目也用了同樣的第三方庫(kù)油啤,在編譯的時(shí)候就會(huì)有 duplicate symbol 錯(cuò)誤典徘,因?yàn)橛袃煞萃瑯拥牡谌綆?kù)。解決辦法就是把用到的第三方庫(kù)加上自定義前綴益咬,包括類(lèi)名逮诲、delegate 協(xié)議、常量名,尤其需要注意 Category 的方法名要修改梅鹦。
9裆甩、封裝靜態(tài)庫(kù)的時(shí)候應(yīng)盡量避免引入重量級(jí)第三方庫(kù),**多自己進(jìn)行封裝**齐唆。
10嗤栓、一個(gè)靜態(tài)庫(kù)要**有自己獨(dú)有的前綴**,所有類(lèi)名箍邮、常量等都要加同樣的前綴茉帅。
11、**真機(jī)+模擬器支持**锭弊。(和第2條意思一樣)Xcode 默認(rèn)只會(huì)用當(dāng)前環(huán)境(真機(jī)或模擬器)生成靜態(tài)庫(kù)堪澎,這樣的 SDK 不方便其他項(xiàng)目開(kāi)發(fā)時(shí)調(diào)試。解決辦法就是通過(guò)腳本生成一份通用庫(kù)味滞,build_universal_library.sh樱蛤,via SO.
12、**文檔**桃犬。靜態(tài)庫(kù)的方便是使用者直接拿你提供的方法來(lái)用,無(wú)需關(guān)注具體實(shí)現(xiàn)行楞;不方便在于看不到實(shí)現(xiàn)攒暇,出現(xiàn)問(wèn)題無(wú)法排查,因此需要把 SDK 的版本子房、更新歷史形用、使用、FAQ 等寫(xiě)成文檔证杭,方便使用田度,也顯得 SDK 比較正式規(guī)范。
13解愤、圖片等資源文件用 **bundle** 方式打包镇饺。一個(gè)簡(jiǎn)單制作 bundle 的方法:新建文件夾,重命名為 YourSDK.bundle送讲,然后 Show Package Contents 打開(kāi)奸笤,加入圖片。使用圖片的時(shí)候需要指明 bundle: [UIImage imageNamed:@"YourSDK.bundle/img.png"]哼鬓。也可以用 Target 方式制作 bundle监右。
14、如果 SDK 有用到 **Category**异希,注意項(xiàng)目設(shè)置 Other Linker Flags 添加 -ObjC健盒。
編譯過(guò)程:
從C代碼到可執(zhí)行文件經(jīng)歷的步驟是:源代碼 > 預(yù)處理器 > 編譯器 > 匯編器 > 機(jī)器碼 > 鏈接器 > 可執(zhí)行文件
在最后一步需要把.o文件和C語(yǔ)言運(yùn)行庫(kù)鏈接起來(lái),這時(shí)候需要用到ld命令。源文件經(jīng)過(guò)一系列處理以后扣癣,會(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類(lèi)和分類(lèi)都加載到最后的可執(zhí)行文件中
-all_load:會(huì)讓鏈接器把所有找到的目標(biāo)文件都加載到可執(zhí)行文件中垂涯,但是千萬(wàn)不要隨便使用這個(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ù)文件的按需加載
研究不深勒极,歡迎各位來(lái)指點(diǎn)是掰。