因?yàn)楣咀罱涯骋粋€(gè)功能模塊提供給第三方使用,所以就需要將涉及到的源碼打包成靜態(tài)庫(.a文件),但是。。。怎么打包呢,從來沒做過?。」雀枇艘簧衔?,找到很多文章,寫的都很好,但是很少能有一篇,讓你看到就能徹底搞懂的。比如自己源碼中包含第三方靜態(tài)庫怎么辦?用到的圖片資源,Xib資源怎么辦?自己源碼依賴的第三方庫怎么處理才能不影響第三方對(duì)相同庫的升級(jí)及使用?這都是需要考慮的問題。關(guān)于靜態(tài)庫的介紹我就不普及了,直接一步步進(jìn)行.a庫的創(chuàng)建:
#一、創(chuàng)建靜態(tài)庫工程
這一步應(yīng)該是很簡(jiǎn)單的,比如項(xiàng)目名稱取為 YJEChatSDK。

建好之后大概就是這個(gè)樣子:

把工程里默認(rèn)添加的類刪除(移到廢紙簍)就可以了,整個(gè)工程很干凈!

#二、對(duì)工程進(jìn)行必要的設(shè)置
1、支持的系統(tǒng)這里要選擇IOS哦(雖然默認(rèn)就是)。。。

2、Build Active Architecture Only選項(xiàng),當(dāng)它設(shè)置為Yes時(shí),是為了debug的時(shí)候編譯速度更快,此時(shí)它只編譯當(dāng)前的architecture版本。 而設(shè)置為No時(shí),會(huì)編譯所有的版本。這里我們要設(shè)置為NO。

3、如果你的源碼中使用了類別(category),就要在 Other Linker Flags 中添加 -ObjC或者-all_load 。

Unix的標(biāo)準(zhǔn)靜態(tài)庫實(shí)現(xiàn)和Objective-C的動(dòng)態(tài)特性之間有一些沖突:Objective-C沒有為每個(gè)函數(shù)(或者方法)定義鏈接符號(hào),它只為每個(gè)類創(chuàng)建鏈接符號(hào)。這樣當(dāng)在一個(gè)靜態(tài)庫中使用類別來擴(kuò)展已有類的時(shí)候,鏈接器不知道如何把類原有的方法和類別中的方法整合起來,就會(huì)導(dǎo)致你調(diào)用類別中的方法時(shí),出現(xiàn)"selector not recognized",也就是找不到方法定義的錯(cuò)誤。為了解決這個(gè)問題,引入了-ObjC標(biāo)志,它的作用就是將靜態(tài)庫中所有的和對(duì)象相關(guān)的文件都加載進(jìn)來。
本來這樣就可以解決問題了,不過在64位的Mac系統(tǒng)或者iOS系統(tǒng)下,鏈接器有一個(gè)bug,會(huì)導(dǎo)致只包含有類別的靜態(tài)庫無法使用-ObjC標(biāo)志來加載文件,變通方法就是使用-all_load 。
4、選擇release


到此為止對(duì)于工程的基本設(shè)置算是完了,當(dāng)然根據(jù)自己的需求也許還有其他的設(shè)置。
#三、添加代碼文件
注意下,打包靜態(tài)庫的時(shí)候并不能包含資源文件,即使我們將資源文件(.png文件或者Xib文件)拷貝到靜態(tài)庫工程中,但實(shí)際上這些資源是不會(huì)添加到target的,也就是說編譯結(jié)果中并不包含這些資源,因此如果有人調(diào)用你制作的這個(gè)靜態(tài)庫,所有的資源(圖片、Xib)都是缺失的。
針對(duì)這種情況,我們將代碼文件和資源文件分開考慮,首先是代碼文件的打包:
1、添加自己的代碼和私有庫
把自己的源碼以及私有庫(不包括第三方開源庫,因?yàn)殚_源庫誰都可以去下載使用,沒必要也不應(yīng)該打包進(jìn)去)放到工程中。
如果你的源碼或者私有庫又使用了第三方的靜態(tài)庫,打包的時(shí)候并不能靜態(tài)庫中包含靜態(tài)庫,所以只需要把第三方靜態(tài)庫的頭文件放進(jìn)我們的工程就好,而不需要將.a文件也添加進(jìn)來。需要注意的是,最后使用時(shí),要把我們的.a庫和第三方的這個(gè).a庫一起放進(jìn)項(xiàng)目。

2、添加依賴的開源庫
如果我們的源碼依賴一些開源庫,比如AFNetWorking,Mansory等,就需要將它們打包進(jìn)來,不過這樣一來,容易給使用者帶來問題,比如開源庫的沖突,版本不兼容等。
所以我的處理辦法是,只添加這些開源庫的頭文件,然后告訴使用者我們的靜態(tài)庫是依賴這些開源庫的,讓他們自己下載相應(yīng)的框架。這樣做的好處是,方便第三方使用者隨意升級(jí)自己的開源庫而不用擔(dān)心會(huì)跟靜態(tài)庫中的開源庫引起沖突,而且就算使用者升級(jí)了開源庫的版本,也一般不會(huì)改變頭文件里面的接口(高版本總會(huì)兼容低版本),所以不會(huì)影響我們的靜態(tài)庫。

3、暴露出相應(yīng)的接口(頭文件),供第三方使用
我們打包靜態(tài)庫肯定是要給人用的,所以需要暴露出設(shè)計(jì)好的頭文件供別人使用。靜態(tài)工程里需要編譯的所有源文件都會(huì)包含在Compile Source中,如下圖所示:

而需要暴露出來的頭文件添加在Copy Files選項(xiàng)中,如下圖所示:

到此為止基本上完成了打包的準(zhǔn)備工作,下面開始編譯。
#四、編譯生成靜態(tài)庫
根據(jù)需要,如果要在模擬器下使用靜態(tài)庫,編譯時(shí)就選擇模擬器,在真機(jī)中使用,編譯時(shí)就選擇真機(jī)。


這里我在模擬器和真機(jī)下分別運(yùn)行了一次,得到兩個(gè).a文件,真機(jī)和模擬器:

一般將靜態(tài)庫給別人使用時(shí),要同時(shí)給真機(jī)版和模擬器版,給兩個(gè)文件肯定是不方便的,所以要把兩個(gè)版本合并為一個(gè).a文件,這樣不管真機(jī)還是模擬器,都可以運(yùn)行。
打開終端,可以先查看下.a庫支持的架構(gòu),輸入 lipo -info 靜態(tài)庫路徑,靜態(tài)庫路徑這里直接把文件拖進(jìn)來就好。

很顯然,armv7和arm64表示32位和64位真機(jī),i386和x86_64表示32位和64位模擬器(mac的架構(gòu))。下面使用命令將真機(jī)版和模擬器版本合并:
lipo -create 靜態(tài)庫路徑1 靜態(tài)庫路徑2 -output /Users/yangjie/Desktop/YJEChatSDK.a

最終得到需要的YJEChatSDK.a文件。
#處理資源文件(圖片.png和Xib文件)
*首先僅考慮圖片
如果代碼中使用了一些圖片資源,怎么樣才能正確讀取這些圖片呢?我的做法是(我不太清楚是不是還有其他辦法):在桌面新建一個(gè)文件夾,將用到的所有圖片放進(jìn)去,然后把文件夾的名字改為:文件名稱.Bundle。比如我這里就改為YJsdkBundle.Bundle。這樣在使用靜態(tài)庫時(shí),直接將該Bundle文件一起放進(jìn)工程就可以了。

然而這樣還是不行的,因?yàn)殪o態(tài)庫中的代碼依然找不到圖片資源的路徑,所以還是不能正確加載。我們需要重新返回代碼,在代碼中添加兩個(gè)宏定義:
define BUNDLE_PATH [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"YJsdkBundle.Bundle"]
define YJImageNamed(imageName) ([UIImage imageNamed:[NSString stringWithFormat:@"%@/%@",BUNDLE_PATH,imageName]])
將所有加載圖片的方法[UIImage imageNamed:]替換為我們定義的宏,比如我這里是YJImageNamed(圖片名稱),需要注意的是,圖片名稱要跟Bundle文件中圖片名稱對(duì)應(yīng)(后綴@2x或者@3x不用加)。我這里的Bundle文件內(nèi)并沒有子文件夾,所以宏定義中,stringWithFormat:@"%@/%@",BUNDLE_PATH,imageName這么寫,如果你的Bundle文件下還有子文件夾,應(yīng)該這么寫:
stringWithFormat:@"%@/文件夾名稱/%@",BUNDLE_PATH,imageName
這樣,圖片就可以正確加載了!
關(guān)于Xib文件
對(duì)于Xib文件,我并沒有親自嘗試,所以。。。沒有發(fā)言權(quán)了,大家可以谷歌一下。(__*)
打包好之后,使用時(shí)編譯連接錯(cuò)誤問題:


類似這種奇怪的錯(cuò)誤,我遇到了,原因是,1、沒有引入必要的系統(tǒng)框架。2、靜態(tài)庫已經(jīng)包含了某些文件,使用的時(shí)候,又引入了這些文件,造成duplicate錯(cuò)誤。