庫介紹app
庫從本質上來講是一種可執行代碼的二進制格式,能夠被載入內存中執行。庫分靜態庫和動態庫兩種。框架
iOS中的靜態庫有 .a 和 .framework兩種形式;動態庫有.dylib 和 .framework 形式,後來.dylib動態庫又被蘋果替換成.tbd的形式。iphone
靜態庫與動態庫的區別ide
靜態庫和動態庫是相對編譯期和運行期的:靜態庫在程序編譯時會被連接到目標代碼中,程序運行時將再也不須要改靜態庫;而動態庫在程序編譯時並不會被連接到目標代碼中,只是在程序運行時才被載入,由於在程序運行期間還須要動態庫的存在。ui
總結:同一個靜態庫在不一樣程序中使用時,每個程序中都得導入一次,打包時也被打包進去,造成一個程序。而動態庫在不一樣程序中,打包時並無被打包進去,只在程序運行使用時,才連接載入(如系統的框架如UIKit、Foundation等),因此程序體積會小不少,可是蘋果不讓使用本身的動態庫,不然審覈就沒法經過。spa
建立.a靜態庫3d
第一步,新建工程。通常使用工程名就使用庫的名稱,好比我這裏用FMDB來建立靜態庫,個人工程名就取名爲FMDB,建立的.a靜態庫就是libFMDB.a。code
第二步,刪除系統默認建立的【FMDB.h】和【FMDB.m】文件,導入須要打包的源文件。blog
第三步(方式一),修改項目配置圖片
點擊上圖中的【3】,彈出的列表中選擇【New Headers Phase】,打開【Headers (0 items)】,點擊左下角的【+】,選擇全部的.h文件。
第三步(方式二),修改項目配置
第四步,修改導出product配置
第五步,修改編譯指令集
模擬器:iPhone4s~5 : i386 iPhone5s~6plus : x86_64
真機:iPhone3gs~4s : armv7 iPhone5~5c : armv7s iPhone5s~6plus : arm64
若是第五步這裏,設置爲YES,那麼編譯出來的.a靜態庫就只包含當前設備的指令集。
舉個例子:若是咱們選擇iPhone 5模擬器【Command+B】編譯,則編譯出來的.a靜態庫只能用iPhone4s~5模擬器跑程序,用iPhone5s~6plus,則會報找不到x86_64的libFMDB庫。
設置爲NO,則會把全部指令集的都打包合併。
第六步,編譯(快捷鍵【Command+B】
編譯時,須要用模擬器和真機各編譯一次,這樣Products目錄下的libFMDB.a靜態庫纔會變爲黑色,右鍵show in Finder,能夠進入Products目錄下。
爲何須要用模擬器和真機各編譯一次呢?
能夠看到Products目錄下有【Release-iphoneos】和【Release-iphonesimulator】兩個文件件。前者裏面是真機使用的.a靜態庫,後者是模擬器使用的.a靜態庫。
注意:若是步驟四中,不將Build Configuration改成Release,則打包出來的靜態庫會存於【Debug-iphoneos】和【Debug-iphonesimulator】兩個文件夾下。
咱們通常都使用Release模式,由於程序最終發佈以後是Release版的,因此靜態庫也是在Release模式下使用。
若是想要通用須要將模擬器使用的靜態庫與真機使用的靜態庫合併成一個靜態庫,可使用終端命令來實現。命令格式:
lipo -create 第一個.a文件的絕對路徑 第二個.a文件的絕對路徑 -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
補充:通過屢次實踐,第三步的操做省略,依然能夠導出可正常使用的包。
若是靜態庫中有category類,則在使用靜態庫的項目配置中【Other Linker Flags】須要添加參數【-ObjC]或者【-all_load】。
建立framework靜態庫
第一步,新建項目
第二步,刪除系統默認建立的【FMDB.h】和【FMDB.m】文件,導入須要打包的源文件。
第三步,修改項目配置
首先,設置須要暴漏的頭文件
這裏須要注意的是暴露出來的頭文件中import的其餘類也得添加到public中暴露出來。
若是不想將import的類暴露出來,那麼在頭文件中用@class 而後在對應的.m文件中再import。
而後設置編譯模式,在Xcode菜單【Product】--->【Scheme】--->【Edit Scheme...】中
設置編譯出的靜態庫包含的指令集
最後修改生成的Mach-O格式
第四步,編譯生成靜態庫
編譯時,須要用模擬器和真機各編譯一次,這樣Products目錄下的libFMDB.a靜態庫纔會變爲黑色,右鍵show in Finder,能夠進入Products目錄下。
第五步,合併模擬器版framework和真機版framework
合併的命令同上面類似,不一樣之處是:framework靜態庫合併的不是framework,而是framework下的一個二進制文件,即上一步圖中標記的文件。
lipo -create 第一個framework下二進制文件的絕對路徑 第二個framework下二進制文件的絕對路徑 -output 最終的二進制文件路徑。
本文中使用的命令以下:
1
|
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
|
最後將任何一個framework中的二進制文件替換成合並後的二進制文件便可。
把framework添加到要使用的項目中便可使用。
注意:若是建立的framework中使用了category類,則在使用framework的項目配置中【Other Linker Flags】須要添加參數【-ObjC]或者【-all_load】。
若是使用framework的使用出現【Umbrella header for module 'XXXX' does not include header 'XXXXX.h'】,是由於錯把xxxxx.h拖到了public中。
若是出現【dyld: Library not loaded:XXXXXX】,是由於打包的framework版本過高。好比打包framework時,選擇的是iOS 9.0,而實際的工程環境是iOS 8開始的。
若是建立的framework類中使用了.dylib或者.tbd,首先須要在實際項目中導入.dylib或者.tbd動態庫,而後須要設置【Allow Non-modular Includes ....】爲YES,不然會報錯"Include of non-modular header inside framework module"。
補充:打包成的靜態庫確定是比源碼類要大不少的,由於是由不一樣指令集不一樣設備的版本合併成的。因此若是你很在乎你的app大小,而且也不是很須要打包成靜態庫的話,仍是用原始類吧。
framework靜態庫中是能夠包含圖片資源的;而.a靜態庫中不能包含圖片資源,只能另外建立一個目錄存放。
填坑記錄
上面的注意裏提到了一些坑,以及解決辦法。這裏再記錄一些:
1.framework中用到了NSClassFromString,可是轉換出來的class 一直爲nil。
先來看一下這個API的官方描述
什麼意思呢?若是轉換出來的class爲nil,有兩種狀況:一種狀況是這個類不存在;第二種狀況是這個類尚未被load。因此通常出現問題,都是第二種狀況。
怎麼解決這個問題呢?在主工程的【Other Linker Flags】須要添加參數【-ObjC]便可。