Xcode7 中創建靜態庫:.a 和 .framework
一、簡單介紹
1.什么是庫?
庫是程序代碼的集合,是共享程序代碼的一種方式
2.庫的分類
根據源代碼的公開情況,庫可以分為2種類型
(1)開源庫
公開源代碼,能看到具體實現
比如SDWebImage、AFNetworking
(2)閉源庫
不公開源代碼,是經過編譯后的二進制文件,看不到具體實現
主要分為:靜態庫、動態庫
二、靜態庫和動態庫
1.靜態庫和動態庫的存在形式
靜態庫:.a 和 .framework
動態庫:.dylib 和 .framework
2.靜態庫和動態庫在使用上的區別
靜態庫:鏈接時,靜態庫會被完整地復制到可執行文件中,被多次使用就有多份冗余拷貝(圖1所示)
動態庫:鏈接時不復制,程序運行時由系統動態加載到內存,供程序調用,系統只加載一次,多個程序共用,節省內存(圖2所示)

三、靜態庫簡介:
1、靜態庫分為:

真機—Debug(調試)版本、
真機—Release(發布)版本、
模擬器—Debug版本、
模擬器—Release版本;
開發中一般都打包Release(發布)版本,將真機和模擬器版本合並,提供外界。
2、使用場景:在項目開發的過程中,例如兩個公司之間業務交流,不可能把源代碼都發送給另一個公司,這時候將私密內容打包成靜態庫,別人只能調用接口,而不能知道其中實現的細節。
四、用Xcode7創建.a靜態庫
.a文件版本(以制作SDWebImage靜態庫為例)
1、新建項目,點擊iOS—Framework&—Cocoa Touch Static Library。

給你的工程命名為SDWebImageStaticLib

2、系統自動生成以工程名命名的.h和.m文件,可自定義的在目錄下添加或刪除文件,注意目錄下Products文件夾有一個.a文件為紅色,說明文件並不存在,需要接下來的處理就是將是自己寫的庫文件中所有的.m文件都放入.a文件中私有隱藏起來。

這里我們將系統生成的.h和.m文件刪除。


3.選中TARGETS下的庫文件名,創建對外暴露的頭文件

創建完之后

4.將SDWebImage下的所有文件導入到SDWebImageStaticLib下

注意:第三步驟和第四步驟是可以調換的,只不過調換后需要手動的將需要暴露的頭文件添加進去,所以不調換可以幫助我們完成。
此時默認的情況下,暴露的頭文件都在Project下,應該移動到Public下
之前:

移動后:

5、然后點擊左上角,選擇Edit Scheme,Build Configuration下選擇Release,先注意檢查下面Release是否為NO:Yes表示只編譯選中模擬器設備對應的架構,No則為編譯所有模擬器設備支持的cup架構(Debug版本同理),選擇NO,然后分別在模擬器和真機下Command+B編譯一下,會看到Products文件夾下的.a文件變為黑色,這個.a文件就是我們想要得到的靜態庫,這里會出現一個問題你先編譯的模擬器會發現.a依然是紅色,你需要模擬器和真機都編譯后.a才會變成黑色,這應該是Xcode本身的問題。
<1>

<2>

<3>

這3步設置完后,選擇真機模式編譯一下,Command + B,此時看到.a靜態庫文件變成了黑色。

然后將上面的第<3>步改為Debug,再選擇模擬器編譯一下,Command + B,就創建了模擬器環境下的.a文件,截圖如下一樣。
可以在終端看到,真機Release發布模式和模擬器Debug測試模式的.a文件都存在

文件為:


6、合並真機和模擬器.a文件,在終端輸入以下命令行:lipo -create 模擬器.a文件的路徑 真機.a文件的路徑 -output 合並后的保存路徑(最終會得到一個合並后的libSDWebImageStaticLib.a文件,再將暴露出來的.h頭文件一起復制出來。
7、使用:只需將libSDWebImageStaticLib.a和暴露出來的.h頭文件導入工程目錄下就可供外界使用,這樣就很好的保護了自己的實現源代碼,不被他人篡改和偷竊。

8.這里再補充一下:
<1>.文件大小.a文件的體積(一般情況下)
真機用的.a > 模擬器用的.a
所合成.a == 真機用的.a + 模擬器用的.a
<2>.
注:關於靜態庫對CPU架構的支持,首先了解iOS設備CPU架構方面的知識,ARM是微處理器行業的一家知名企業,arm處理器以體積小和高性能的優勢在嵌入式設備中廣泛使用,幾乎所有手機都是使用它的。
模擬器:iphone4s~5 : i386 iphone5s~6plus : x86_64
真機:iphone3gs~4s : armv7 iphone5~5c : armv7s (靜態庫只要支持了armv7,就可以跑在armv7s的架構上) iphone5s~6plus : arm64
armv6, armv7, armv7s是ARM CPU的不同指令集,原則是向下兼容的。例如iPhone4S CPU支持armv7, 但它同時兼容armv6,只是使用armv6指令可能無法充分發揮它的特性。
查看靜態庫.a對處理器架構的支持,先cd到.a文件的路徑下,命令行輸入:lipo -info xxxxx.a

<3>.
如果庫中還包含了一些資源文件(如圖片等),那么資源文件也應該放在上面的文件夾中。
五、用Xcode7創建.framework靜態庫,以MJRefresh為例
1.新建工程並選擇默認Target為Cocoa Touch Framework, 如圖:

2.給你的工程命名為MJRefresh,生成的靜態庫將以標准的名稱格式出現.即MJRefresh.framework

3、項目自動創建設置開放的頭文件和存放資源的Bundle文件,可以看到一個紅色的MJRefresh.framework空的靜態庫

4、刪除自動生成的MJRefresh.h文件

5、導入MJRefresh框架中的所有文件,應該暴露出來的.h文件默認都在Project下

6、.framework中有些類可能是一些私有的輔助工具,不需要使用者看到,在這里只需要把開放出去的類放到Public下, 如圖

7、然后選擇模擬器和Debug模式,編譯一下,Command + B,此時,雖然看到MJRefresh.framework仍為紅色,其實已經生成能在模擬器上運行的.framework靜態庫,進入MJRefresh.framework目錄文件下,以及使用終端可以看到生成的靜態庫。
選中靜態庫文件,獲取絕對路徑如下:

在目錄下和終端可以看到生成的靜態庫


8、再選擇真機,設置Release模式,編譯一下,Command + B,此時,雖然看到MJRefresh.framework變成黑色,生成能在真機上運行的靜態庫文件。


在目錄下和終端可以看到生成的靜態庫


9、我們隨意選擇一個靜態庫,點進去看這個庫文件中的具體內容,可以看到一個MJRefresh可執行二進制的文件,資源MJRefresh.bundle,暴露出去的頭文件Headers等

10、下面一步就是合並了,生成模擬器和真機環境下通用的二進制可執行文件MJRefresh,生成后看一下這個可執行文件使用的微處理器架構有哪些:

11.好了,合並替換完成,剩下的就是如何使用這個最終的MJRefresh.framework靜態庫了。
(1)我們可以隨意選擇一個生成的MJRefresh.framework,拷貝一下到桌面

(2)然后再將合並后生成的可執二進制文件MJRefresh拷貝一下,粘貼到桌面上這個MJRefresh.framework文件中,替換里面的MJRefresh,生成一個完成的靜態庫。

(3)打開MJRefresh.framework/Modules/module.modulemap文件,可以看到暴露的MJRefrsh.h文件被放在umbrella雨傘下保護起來了,所以我們需要將其他的所有暴露的.h文件放到MJRefresh.h文件中保護起來,不然會出現警告


(4)我們以為此時大功告成,可以測試代碼了,結果出現如下錯誤:

為什么會這樣的?因為我們做的是靜態庫,在使用的時候需要額外加一個步驟,要把Framework同時添加到‘Embedded Binaries’中


此時再測試,發現沒問題了,大功告成,可喜可賀!

六、制作靜態庫的注意點
(1)注意:
無論是 .a 靜態庫還是 .framework 靜態庫,最終需要的都是:二進制文件 + .h + 其它資源文件
(2).a 和 .framework 的使用區別
.a 本身是一個二進制文件,需要配上 .h 和 其它資源文件 才能使用
.framework 本身已經包含了 .h 和 其它資源文件,可以直接使用
(3)圖片資源的處理
如果靜態庫中用到了圖片資源,一般都放到一個bundle文件中,bundle名字一般跟 .a 或 .framework 名字一致
bundle的創建:新建一個文件夾,修改擴展名為 .bundle 即可,右擊bundle文件,顯示包內容,就可以往bundle文件中放東西
建議:自己制作的靜態庫中要用到的圖片資源,不建議直接以png的后綴名方式拖到項目中使用,而是推薦使用放到bundle文件中。這樣可以避免靜態庫的圖片名和使用靜態庫的項目中存在的圖片產生沖突。
1)新建一個文件夾,把需要打包的資源圖片放在里面
例如:

2)修改擴展名為 .bundle,敲回車,點擊添加。
例如:

(4)多文件處理
如果靜態庫需要暴露出來的 .h 比較多,可以考慮創建一個主頭文件(一般 主頭文件 和 靜態庫 同名)
在主頭文件中包含所有其他需要暴露出來的 .h 文件
使用靜態庫時,只需要#import 主頭文件
實際上蘋果官方就是這么做的,例如:#import <UIKit/UIKit.h>
(5).framework為什么既是靜態庫又是動態庫
系統的 .framework 是動態庫
我們自己建立的 .framework 是靜態庫
(6)靜態庫中包含了Category(分類)
如果靜態庫中包含了Category,有時候在使用靜態庫的工程中會報“方法找不到”的錯誤(unrecognized selector sent to instance)
解決方案:在使用靜態庫的工程中配置Other Linker Flags為-ObjC

