iOS中的靜態(tài)庫(kù)有 .a 和 .framework兩種形式厚棵;動(dòng)態(tài)庫(kù)有.dylib 和 .framework 形式蕉世,后來(lái).dylib動(dòng)態(tài)庫(kù)又被蘋果替換成.tbd的形式。
靜態(tài)庫(kù)與動(dòng)態(tài)庫(kù)的區(qū)別
靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)是相對(duì)編譯期和運(yùn)行期的:靜態(tài)庫(kù)在程序編譯時(shí)會(huì)被鏈接到目標(biāo)代碼中婆硬,程序運(yùn)行時(shí)將不再需要改靜態(tài)庫(kù)狠轻;而動(dòng)態(tài)庫(kù)在程序編譯時(shí)并不會(huì)被鏈接到目標(biāo)代碼中,只是在程序運(yùn)行時(shí)才被載入彬犯,因?yàn)樵诔绦蜻\(yùn)行期間還需要?jiǎng)討B(tài)庫(kù)的存在向楼。
總結(jié):同一個(gè)靜態(tài)庫(kù)在不同程序中使用時(shí)查吊,每一個(gè)程序中都得導(dǎo)入一次,打包時(shí)也被打包進(jìn)去湖蜕,形成一個(gè)程序逻卖。而動(dòng)態(tài)庫(kù)在不同程序中,打包時(shí)并沒(méi)有被打包進(jìn)去昭抒,只在程序運(yùn)行使用時(shí)评也,才鏈接載入(如系統(tǒng)的框架如UIKit、Foundation等)灭返,所以程序體積會(huì)小很多盗迟,但是蘋果不讓使用自己的動(dòng)態(tài)庫(kù),否則審核就無(wú)法通過(guò)熙含。
創(chuàng)建.a靜態(tài)庫(kù)
- 第一步罚缕,新建工程。一般使用工程名就使用庫(kù)的名稱怎静,比如我這里用FMDB來(lái)創(chuàng)建靜態(tài)庫(kù)邮弹,我的工程名就取名為FMDB,創(chuàng)建的.a靜態(tài)庫(kù)就是libFMDB.a消约。
- 第二步肠鲫,刪除系統(tǒng)默認(rèn)創(chuàng)建的【FMDB.h】和【FMDB.m】文件,導(dǎo)入需要打包的源文件或粮。
- 第三步
(方式一)导饲,修改項(xiàng)目配置
點(diǎn)擊上圖中的【3】,彈出的列表中選擇【New Headers Phase】,打開【Headers (0 items)】氯材,點(diǎn)擊左下角的【+】渣锦,選擇所有的.h文件。
(方式二)氢哮,修改項(xiàng)目配置
- 第四步袋毙,修改導(dǎo)出product配置
- 第五步,修改編譯指令集
模擬器:iPhone4s~5 : i386 iPhone5s~6plus : x86_64
真機(jī):iPhone3gs~4s : armv7 iPhone5~5c : armv7s iPhone5s~6plus : arm64
如果第五步這里冗尤,設(shè)置為YES听盖,那么編譯出來(lái)的.a靜態(tài)庫(kù)就只包含當(dāng)前設(shè)備的指令集。
舉個(gè)例子:如果我們選擇iPhone 5模擬器【Command+B】編譯裂七,則編譯出來(lái)的.a靜態(tài)庫(kù)只能用iPhone4s5模擬器跑程序皆看,用iPhone5s6plus,則會(huì)報(bào)找不到x86_64的libFMDB庫(kù)背零。
設(shè)置為NO腰吟,則會(huì)把所有指令集的都打包合并!!!!
- 第六步,編譯(快捷鍵【Command+B】
編譯時(shí)徙瓶,需要用模擬器和真機(jī)各編譯一次毛雇,這樣Products目錄下的libFMDB.a靜態(tài)庫(kù)才會(huì)變?yōu)楹谏党疲益Ishow in Finder,可以進(jìn)入Products目錄下灵疮。
為什么需要用模擬器和真機(jī)各編譯一次呢织阅?
可以看到Products目錄下有【Release-iphoneos】和【Release-iphonesimulator】?jī)蓚€(gè)文件件。前者里面是真機(jī)使用的.a靜態(tài)庫(kù)始藕,后者是模擬器使用的.a靜態(tài)庫(kù)蒲稳。
注意:如果步驟四中,不將Build Configuration改為Release,則打包出來(lái)的靜態(tài)庫(kù)會(huì)存于【Debug-iphoneos】和【Debug-iphonesimulator】?jī)蓚€(gè)文件夾下伍派。
我們一般都使用Release模式江耀,因?yàn)槌绦蜃罱K發(fā)布之后是Release版的,所以靜態(tài)庫(kù)也是在Release模式下使用诉植。
如果想要通用需要將模擬器使用的靜態(tài)庫(kù)與真機(jī)使用的靜態(tài)庫(kù)合并成一個(gè)靜態(tài)庫(kù)祥国,可以使用終端命令來(lái)實(shí)現(xiàn)。命令格式:
lipo -create
第一個(gè).a文件的絕對(duì)路徑 第二個(gè).a文件的絕對(duì)路徑 -output 最終的.a文件路徑晾腔。
本文中使用的命令如下:
lipo -create /Users/harvey/Library/Developer/Xcode/DerivedData/FMDB-ctegiztcjikewoeprxxtmryzetfa/Build/Products/Release-iphoneos/libFMDB.a /Users/harvey/Library/Developer/Xcode/DerivedData/FMDB-ctegiztcjikewoeprxxtmryzetfa/Build/Products/Release-iphonesimulator/libFMDB.a -output /Users/harvey/Desktop/libFMDB.a
補(bǔ)充:經(jīng)過(guò)多次實(shí)踐舌稀,第三步的操作省略,依然可以導(dǎo)出可正常使用的包灼擂。
如果靜態(tài)庫(kù)中有category類壁查,則在使用靜態(tài)庫(kù)的項(xiàng)目配置中【Other Linker Flags】需要添加參數(shù)-ObjC
或者-all_load
。
創(chuàng)建framework靜態(tài)庫(kù)
- 第一步剔应,新建項(xiàng)目
- 第二步睡腿,刪除系統(tǒng)默認(rèn)創(chuàng)建的【FMDB.h】和【FMDB.m】文件,導(dǎo)入需要打包的源文件峻贮。
- 第三步席怪,修改項(xiàng)目配置
首先,設(shè)置需要暴漏的頭文件
這里需要注意的是暴露出來(lái)的頭文件中import的其他類也得添加到public中暴露出來(lái)纤控。
如果不想將import的類暴露出來(lái)挂捻,那么在頭文件中用@class 然后在對(duì)應(yīng)的.m文件中再import。
然后設(shè)置編譯模式船万,在Xcode菜單【Product】--->【Scheme】--->【Edit Scheme...】中
設(shè)置編譯出的靜態(tài)庫(kù)包含的指令集
最后修改生成的Mach-O格式
- 第四步刻撒,編譯生成靜態(tài)庫(kù)
編譯時(shí),需要用模擬器和真機(jī)各編譯一次耿导,這樣Products目錄下的libFMDB.a靜態(tài)庫(kù)才會(huì)變?yōu)楹谏益Ishow in Finder,可以進(jìn)入Products目錄下碎节。
- 第五步捧搞,合并模擬器版framework和真機(jī)版framework
合并的命令同上面相似抵卫,不同之處是:framework靜態(tài)庫(kù)合并的不是framework,而是framework下的一個(gè)二進(jìn)制文件狮荔,即上一步圖中標(biāo)記的文件胎撇。
lipo -create 第一個(gè)framework下二進(jìn)制文件的絕對(duì)路徑 第二個(gè)framework下二進(jìn)制文件的絕對(duì)路徑 -output 最終的二進(jìn)制文件路徑。
本文中使用的命令如下:
`lipo -create /Users/harvey/Library/Developer/Xcode/DerivedData/FMDB-clvayfrjgytqrbdkyqrtcjkxfeuz/Build/Products/Release-iphonesimulator/FMDB.framework/FMDB /Users/harvey/Library/Developer/Xcode/DerivedData/FMDB-clvayfrjgytqrbdkyqrtcjkxfeuz/Build/Products/Release-iphoneos/Release-iphoneos.framework/FMDB -output /Users/harvey/Desktop/FMDB`
最后將任何一個(gè)framework中的二進(jìn)制文件替換成合并后的二進(jìn)制文件即可殖氏。
把framework添加到要使用的項(xiàng)目中即可使用晚树。
注意:
如果創(chuàng)建的framework中使用了category類,則在使用framework的項(xiàng)目配置中【Other Linker Flags】需要添加參數(shù)【-ObjC]或者【-all_load】雅采。
如果使用framework的使用出現(xiàn)【Umbrella header for module 'XXXX' does not include header 'XXXXX.h'】,是因?yàn)殄e(cuò)把xxxxx.h拖到了public中爵憎。
如果出現(xiàn)【dyld: Library not loaded:XXXXXX】,是因?yàn)榇虬膄ramework版本太高婚瓜。比如打包framework時(shí)宝鼓,選擇的是iOS 9.0,而實(shí)際的工程環(huán)境是iOS 8開始的巴刻。
如果創(chuàng)建的framework類中使用了.dylib或者.tbd愚铡,首先需要在實(shí)際項(xiàng)目中導(dǎo)入.dylib或者.tbd動(dòng)態(tài)庫(kù),然后需要設(shè)置【Allow Non-modular Includes ....】為YES胡陪,否則會(huì)報(bào)錯(cuò)"Include of non-modular header inside framework module"沥寥。
補(bǔ)充:
打包成的靜態(tài)庫(kù)肯定是比源碼類要大很多的,因?yàn)槭怯刹煌噶罴煌O(shè)備的版本合并成的柠座。所以如果你很在意你的app大小邑雅,并且也不是很需要打包成靜態(tài)庫(kù)的話,還是用原始類吧妈经。
framework靜態(tài)庫(kù)中是可以包含圖片資源的淮野;而.a靜態(tài)庫(kù)中不能包含圖片資源,只能另外創(chuàng)建一個(gè)目錄存放狂塘。
填坑記錄
上面的注意里提到了一些坑录煤,以及解決辦法。這里再記錄一些:
1.framework中用到了NSClassFromString荞胡,但是轉(zhuǎn)換出來(lái)的class 一直為nil妈踊。
先來(lái)看一下這個(gè)API的官方描述
什么意思呢?如果轉(zhuǎn)換出來(lái)的class為nil泪漂,有兩種情況:一種情況是這個(gè)類不存在廊营;第二種情況是這個(gè)類還沒(méi)有被load。所以一般出現(xiàn)問(wèn)題萝勤,都是第二種情況露筒。
怎么解決這個(gè)問(wèn)題呢?在主工程的【Other Linker Flags】需要添加參數(shù)【-ObjC]即可敌卓。