iOS中的靜態(tài)庫(kù)有 .a 和 .framework兩種形式;動(dòng)態(tài)庫(kù)有.dylib 和 .framework 形式,后來(lái).dylib動(dòng)態(tài)庫(kù)又被蘋(píng)果替換成.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ì)小很多,但是蘋(píng)果不讓使用自己的動(dòng)態(tài)庫(kù),否則審核就無(wú)法通過(guò)。
創(chuàng)建.a靜態(tài)庫(kù)
第一步,新建工程。一般使用工程名就使用庫(kù)的名稱(chēng),比如我這里用FMDB來(lái)創(chuàng)建靜態(tài)庫(kù),我的工程名就取名為FMDB,創(chuàng)建的.a靜態(tài)庫(kù)就是libFMDB.a。
使用靜態(tài)庫(kù)模板新建工程.png
創(chuàng)建的工程.png
第二步,刪除系統(tǒng)默認(rèn)創(chuàng)建的【FMDB.h】和【FMDB.m】文件,導(dǎo)入需要打包的源文件。
導(dǎo)入源文件后.png
第三步(方式一),修改項(xiàng)目配置
修改配置.png
點(diǎn)擊上圖中的【3】,彈出的列表中選擇【New Headers Phase】,打開(kāi)【Headers (0 items)】,點(diǎn)擊左下角的【+】,選擇所有的.h文件。
配置需要暴漏的文件的.h頭.png
第三步(方式二),修改項(xiàng)目配置
修改項(xiàng)目配置.png
第四步,修改導(dǎo)出product配置
修改編譯配置.png
第五步,修改編譯指令集
設(shè)置Release為NO.png
模擬器: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目錄下。
小范筆記:(如果想要要.a文件生成在本工程指定的目錄下,需要將PROJECT-Build Settings-Per-configuration Build Products Path 目錄改成$(SRCROOT)/LibraryDemo/Library就可以("$(SRCROOT)/當(dāng)前工程名字/需要包含頭文件所在文件夾"))
編譯結(jié)果.png
為什么需要用模擬器和真機(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類(lèi),則在使用靜態(tài)庫(kù)的項(xiàng)目配置中【Other Linker Flags】需要添加參數(shù)【-ObjC]或者【-all_load】。
創(chuàng)建framework靜態(tài)庫(kù)
第一步,新建項(xiàng)目
新建項(xiàng)目.png
第二步,刪除系統(tǒng)默認(rèn)創(chuàng)建的【FMDB.h】和【FMDB.m】文件,導(dǎo)入需要打包的源文件。
導(dǎo)入源碼后的工程.png
第三步,修改項(xiàng)目配置首先,設(shè)置需要暴漏的頭文件
header文件設(shè)置.png
這里需要注意的是暴露出來(lái)的頭文件中import的其他類(lèi)也得添加到public中暴露出來(lái)。如果不想將import的類(lèi)暴露出來(lái),那么在頭文件中用@class 然后在對(duì)應(yīng)的.m文件中再import。
然后設(shè)置編譯模式,在Xcode菜單【Product】--->【Scheme】--->【Edit Scheme...】中
設(shè)置編譯模式.png
設(shè)置編譯出的靜態(tài)庫(kù)包含的指令集
設(shè)置編譯出的靜態(tài)庫(kù)包含的指令集.png
最后修改生成的Mach-O格式
修改Mach-O 格式.png
第四步,編譯生成靜態(tài)庫(kù)編譯時(shí),需要用模擬器和真機(jī)各編譯一次,這樣Products目錄下的libFMDB.a靜態(tài)庫(kù)才會(huì)變?yōu)楹谏益Ishow in Finder,可以進(jìn)入Products目錄下。
編譯生成的framework靜態(tài)庫(kù).png
第五步,合并模擬器版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類(lèi),則在使用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開(kāi)始的。
如果創(chuàng)建的framework類(lèi)中使用了.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"。
設(shè)置【Allow Non-modular Includes ....】.png
補(bǔ)充:打包成的靜態(tài)庫(kù)肯定是比源碼類(lèi)要大很多的,因?yàn)槭怯刹煌噶罴煌O(shè)備的版本合并成的。所以如果你很在意你的app大小,并且也不是很需要打包成靜態(tài)庫(kù)的話(huà),還是用原始類(lèi)吧。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è)類(lèi)不存在;第二種情況是這個(gè)類(lèi)還沒(méi)有被load。所以一般出現(xiàn)問(wèn)題,都是第二種情況。怎么解決這個(gè)問(wèn)題呢?在主工程的【Other Linker Flags】需要添加參數(shù)【-ObjC]即可。
2.framework中把圖片、音頻打包進(jìn)bundle中,但是一直加載不到。打包的framework中有一個(gè)bundle,bundle里有一些圖片、音頻等資源。但是用如下方式:
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"XXXX" ofType:@"bundle"];NSString *mp3Path = [[NSBundle bundleWithPath:bundlePath] pathForResource:@"Message_system" ofType:@"mp3"];
獲取的mp3Path一直為nil。framework,只暴露了一些.h頭文件,bundle沒(méi)有暴露出來(lái),無(wú)法獲取。那么我們只需要將bundle與framework一起放入目標(biāo)工程中即可。其實(shí)bundle根本不用打包進(jìn)framework中。例如:我們創(chuàng)建了一個(gè)叫ABC.framework的靜態(tài)庫(kù)。庫(kù)中使用了Message_system.mp3
,那么我們創(chuàng)建一個(gè)bundle,命名為ABC.bundle,然后將Message_system.mp3放入bundle中。打包的時(shí)候,framework并不包含ABC.bundle。最后在要使用ABC.framework的工程中,新建一個(gè)文件夾or group,然后把ABC.framework和ABC.bundle一起拖進(jìn)去,就可以啦。
Have Fun!