寫在前面
過程有點長,但是比較細節,看官各取所需。
創建靜態庫工程
可以先了解iOS庫 .a與.framework區別,靜態庫可以分為.a和.framework類型的的文件。Xcode創建的靜態庫工程,默認編譯后獲得的靜態庫文件是.a類型的。
Xcode(9.1)創建分為動態庫工程和靜態庫工程:
而我們又需要.framework類型的(而對bundle類型的TARGETS進行一些設置,即可得到一個輸出framework文件的工程)具體操作如下:
1)選擇上圖的靜態庫,創建一個靜態庫工程:
2)因為編譯生成.a文件,所以刪除默認的TRAGETS,后面重新添加一個bundle類型的即可。
3)有些教程說,在Product->Scheme->Manage Schemes中,需要刪除關聯的build項(其實只要新建的TARGETS的名稱相同,是不用刪除的,只要新建了,就會重新關聯上):
4)刪除工程文件:
5)這個時候可以新建一個bundle類型的TARGETS了(注意此處是bundle是屬于macOS類型的,后續需要一些設置):
上面說到,新建的時候使用與工程同名的來創建新的TARGETS,即可不用刪除默認的build關聯項:
6)創建完畢,可以看出,bundle在默認情況下,是適用于Mac OS的:
因此需要我們對這個bundle類型的TARGETS做一些設置:
創建bundle類型TARGETS的一些設置:
參考:ios 制作自已的framework
1)
Base SDK:Lstest iOS(iOS XX.XX)
2)指令集設置
Architectures:Standard architectures - $(ARCHS_STANDARD)默認之外,添加一個:armv7s
Valid Architectures: armv7 armv7s arm64
具體原因點這里
3)Dead Code Stripping用于刪除對象文件中不需要加載的符號,減小二進制文件大小(此處為何關閉,我也不知道原因,哪位大神知道的告知一聲)
Dead Code Stripping:NO
4)關聯標準庫(此處關閉,我也不知道原因,哪位大神知道的告知一聲)
Link with Standard Libraries:NO
5)庫類型
可參考:淺談 SDK 開發(一)五種 Mach-O 類型的凜冬之戰
Mac-O Type:Relocatable Object File
6)
在 Packaging 中,將 “Wrapper Extention” 改為“framework”
7)
info文件將 “Bundle OS Type Code” 改為 “FMWK”(Framework 的意思)
8)設置SDK支持的最低系統
Build Settings -> Deployment -> iOS Deployment Target 修改具體參數
也可在 General -> Deployment Info -> Deployment Target 處設置,兩處設置等效
至此,相關設置完成,即可選擇相應環境,Cmd+B 進行編譯,以獲得framework
打開StaticLibObject.framework所在的文件夾,發現這個:
編譯后的framework包,是真機環境和模擬器分開的,其實是因為二者所支持的指令集不同,具體得看你的Architectures設置,必要的時候,可以使用lipo命令來合并兩個文件成一個文件,使其即支持真機,又支持模擬器。
我們查看,或者合并的文件就是這里面的文件:
查看真機的(請看我上面的關于Architectures的設置):
$ lipo -info WxxStaticLibFramework
Architectures in the fat file: WxxStaticLibFramework are: armv7 armv7s arm64
查看模擬器的:
$ lipo -info WxxStaticLibFramework
Non-fat file: WxxStaticLibFramework is architecture: x86_64
修改下模擬器的文件名,將其扔進真機的文件夾,執行合并命令
注意咯:最好合并輸出的文件與原來的保持名字一致,替換掉原來的文件即可。否則framework中會找不到這個文件!!!(修改了名字還能關聯的,有好的辦法請告訴我)
lipo -create 模擬器庫 真機庫 -output 最終庫
$ lipo -create StaticLibObject StaticLibObjectSimulator -output StaticLibLastFramework
查看最終合并的所支持的指令集:
lipo -info StaticLibLastFramework
查詢結果:
Architectures in the fat file: StaticLibLastFramework are: x86_64 armv7 armv7s arm64
StaticLibLastFramework已經可以同時用于真機和模擬器了!可是這種方式有點麻煩,每次重新編譯,都需要重新合并文件,據說還可以用腳本的方式來合并,待我研究后再補充,這里先占個坑。
framework的使用
framework的使用,無非就是對外暴露.h文件,.h文件中有寫好的被人使用的方法。這里拋磚引玉。下面具體說明:
1)新建對外文件,并提供方法 +(void)staticLibSDKTest;
內部方法實現:
+(void)staticLibSDKTest{
NSLog(@"static lib sdk test method");
}
2)暴露頭文件
Build Phases -> + ->New Header Phase
注意:添加完默認是歸類到Project中,將需要暴露的.h文件,手動拖進Public中即可。
重新編譯,即可發現,頭文件已經對外暴露了:
3)創建一個普通工程StaticLibObjectDemo
來測試剛剛創建的framework,跑這個工程的時候,注意真機和模擬器的區別!framework的指令集和工程的運行環境要對上!當然,合并執行真機和執行文件的另當別論了。
引入framework的操作這里無需贅敘了:
4)在使用的地方 #import
#import <StaticLibObject/StaticLibSDK.h>
5)調用StaticLibSDK.h中的方法
[StaticLibSDK staticLibSDKTest];
很開心的快捷鍵Cmd+R,發現報錯了:
具體錯誤:framework not found StaticLibObject
果然,原先的文件被我刪除了,留下的可執行文件是支持模擬器和真機的通用文件StaticLibLastFramework
,連文件名都不一樣的,難怪找不到:
上圖中的同級中有個info.plist文件,本來以為改動里面的Executable file的名字,就可以了,沒想到還是不行!
(無奈,重新合并,合并后的文件與原來的同名稱):
各自環境編譯完成,修改各自的執行文件的名字為:真機的StaticLibObjec1
和模擬器的StaticLibObject2
,執行合并生成StaticLibObject
:
$ lipo -create StaticLibObjec1 StaticLibObject2 -output StaticLibObject
然后把StaticLibObject
文件丟回去.framework文件夾。
再次編譯,即可成功輸出:
StaticLibObjectDemo[1980:867317] static lib sdk test method
至此,整個創建和使用bundle類型創建的framework全部完成。
制作靜態庫的注意事項
從這里搬過來的
1 )注意理解:無論是.a靜態庫還.framework靜態庫,我們需要的都是二進制文件+.h+其它資源文件的形式,不同的是,.a本身就是二進制文件,需要我們自己配上.h和其它文件才能使用,而.framework本身已經包含了.h和其它文件,可以直接使用。
2 )圖片資源的處理:兩種靜態庫,一般都是把圖片文件單獨的放在一個.bundle文件中,一般.bundle的名字和.a或.framework的名字相同。.bundle文件很好弄,新建一個文件夾,把它改名為.bundle就可以了,右鍵,顯示包內容可以向其中添加圖片資源。
3 )category是我們實際開發項目中經常用到的,把category打成靜態庫是沒有問題的,但是在用這個靜態庫的工程中,調用category中的方法時會有找不到該方法的運行時錯誤(selector not recognized),解決辦法是:在使用靜態庫的工程中配置other linker flags的值為-ObjC。
4) 如果一個靜態庫很復雜,需要暴露的.h比較多的話,就可以在靜態庫的內部創建一個.h文件(一般這個.h文件的名字和靜態庫的名字相同),然后把所有需要暴露出來的.h文件都集中放在這個.h文件中,而那些原本需要暴露的.h都不需要再暴露了,只需要把.h暴露出來就可以了。