靜態庫和動態庫的製作以及Bundle資原始檔的使用

weixin_34290000發表於2018-10-15
1154433-8a4089e562ce2638.jpg

前兩天因為公司業務上的需要,我們需要和聯通公司那邊進行業務整合,考慮到一些業務上的隱私性,我們將提供的內容打包成了一個靜態庫,只為對方提供了一些介面,今天正好有空整理一下,填一下這個過程中遇到的一些坑,以方便日後查閱。

什麼是庫?

庫是程式程式碼的集合,是共享程式程式碼的一種方式。我們根據原始碼的公開情況,可以將庫分為 2 種型別

開源庫:公開原始碼,我們能看到程式碼的具體實現,比如SDWebImage、AFNetworking等;如何將自己的程式碼釋出到程式碼託管平臺中,然後藉助CocoaPods供別人使用,我們在上一篇文章中已經講過,這裡不再細說。

閉源庫:不公開原始碼,是經過編譯後的二進位制檔案,看不到程式碼的具體實現。閉源庫主要分為:靜態庫、動態庫

靜態庫和動態庫的存在形式以及使用上的區別

存在形式:

靜態庫:.a 和 .framework

動態庫:.dylib 和 .framework

區別:

靜態庫:連結時,靜態庫會被完整地複製到可執行檔案中, 被多次使用就有多份冗餘拷貝 ,如下圖:

1154433-047ef6cde566f7d2.png

靜態庫

動態庫:連結時不復制,程式執行時由系統動態載入到記憶體,供程式呼叫,系統只載入一次,多個程式共用,以節省記憶體,具體形式如下圖所示:

1154433-660548accc999abf.png

動態庫

靜態庫的使用場景:

專案開發中我們經常使用到的極光推送、百度地圖、友盟分享等等。這些大公司都有自己的核心業務,同時又希望我們去使用他們提供的技術,但是又不想暴露他們的程式碼,因此他們採用"閉源"的方式來讓我們整合。

將MRC的專案放到ARC環境下,我們可以如下圖去操作

1154433-aa25fe2ec719fcfc.png

MRC->ARC

但是如果檔案特別多,我們這樣一個個去操作是不是很麻煩?其實我們可以將MRC的專案,打包成靜態庫, 可以在ARC下直接使用, 不需要轉換,非常方便;

知識準備:

我們都知道模擬器下的靜態庫和真機下的靜態庫是不能混用的,那為什麼呢?其實主要原因是模擬器和真機的CPU架構不一樣(各個模擬器型號之間架構也不一樣)不同機型的CPU, 對應的架構不同;

模擬器:

4s和5使用的是i386

5s-6sPlus使用的是x86_64

真機:

3gs---4s :armv7

5/5c :armv7s(armv7相容armv7s)

5s---6sPlus:arm64

我們可以分別選中不同的模擬器, 進行編譯,然後利用lipo -info xxx.a檢視靜態庫所支援的架構, 如下圖所示:

1154433-e393d3189cb21916.png

檢視靜態庫支援架構

因為CPU架構的不同,這就造成了一個問題,如果我是在6s環境下編譯生成的靜態庫執行到5上面就會編譯不通過, 怎樣可以一次編譯支援多個架構的的靜態庫呢?

可以通過Build Settings -> Build Active -> NO,表示不止編譯活躍的架構, 讓所有的架構都編譯,如下圖所示:

1154433-de49a935a5988933.png

編譯所有架構

.a靜態庫的製作步驟

新建專案-> 選擇 “Cocoa Touch Static Library”

1154433-0f1b95765c11a3ae.png

新建靜態庫

新增庫需要包含的原始碼,並且設定需要暴露的標頭檔案:

1154433-d942f3e5aec348c5.png

3.png

引入製作的靜態庫所需要的網路框架(有些會自動引入,有些需要手動引入,根據編譯報錯,可以檢查。)

1154433-92c86b595a998390.png

引入框架

設定支援所有架構:

1154433-1b96c3688d7fc696.png

設定支援架構

編譯前檢查一下是debug模式還是release模式,選擇release模式。在模擬器和真機中分別編譯後,libzpstaticLibrary.a就由紅色變成了黑色,Show in Finder在目錄中檢視:

1154433-44431926b38a4c2e.png

Release模式

.framework靜態庫的製作步驟

新建專案-> 選擇 “Cocoa Touch Framework”

1154433-f02ae7aa121d8ec2.png

Framework

新增庫需要包含的原始碼,並且設定需要暴露的標頭檔案:

1154433-49abc84bde4d4458.png

暴露標頭檔案

如果使用者需要匯入的標頭檔案過多怎麼辦?我們可以使用一個主標頭檔案包含其他標頭檔案, 讓使用者只匯入一個主標頭檔案。

編譯時, 設定編譯所有架構

1154433-e1299250a685a356.png

設定編譯架構

因為預設製作的是動態庫,我們 需要設定連結型別target -> Build Settings-> 搜尋 Mach-o Type ;改為靜態庫

1154433-e9f565322aaa45b8.png

設定型別

如果沒有這一步,我們在測試的時候會報一個錯誤:

1154433-250a9572b03e7007.png

錯誤提示

這個錯誤是因為我們預設生成的是一個動態庫,如果我們把它當成一個動態庫來用的話 就需要在測試工程中General->Embedded Binaries中匯入我們這個動態庫,這樣就不會報這個錯誤了。

1154433-e37684541084a11f.png

動態庫

經過以上步驟後我們就可以成功的生成了一個.Framework的靜態庫或者是動態庫了。

靜態庫的操作

合併靜態庫

因為靜態庫針對於模擬器和真機生成了不同版本(支援不同架構), 所以沒法同時執行,但是我們可以將二者進行合併,合併後的.a大小大約是不合並的2倍左右。

cd 到一個目錄,然後通過

lipo -create Debug-iphoneos/libTools.a  Debug-iphonesimulator/libTools.a  -output  libTools.a

進行合併,合併後的架構如下圖所示:

1154433-32a3312a8014ba4c.png

分解合併庫

既然有合併靜態庫,那麼對應的也有分解合併庫,例如我們在釋出的時候只想要使用arm64架構,那麼我們可以通過下面這行命令來達到我們目的:

lipo -thin arm64 靜態庫 -output 新的靜態庫名稱

如下圖所示:

1154433-b04ad4044d637a5a.png

檢視分解之後的庫可以發現該庫已經非常小了

從合成庫中移除某個架構

如果合成庫中的某一個架構我們用不到了,想要移除,那麼我們可以通過下面的命令,例如我們想要將合成庫中的i386移除

lipo -remove i386 靜態庫 -output 靜態庫名稱

執行命令後我們可以發現,合成的靜態庫中已經沒有了i386這個架構了

1154433-3501091608407f2e.png

總結

.a靜態庫和.framework靜態庫的區別?

我們都知道靜態庫包括.a和.framework,那麼二者之間到底有什麼區別呢?

.a是一個純二進位制檔案,  而.framework中除了有二進位制檔案之外還有資原始檔,比如Bunle、Plist等。

.a檔案不能直接使用, 至少要有.h檔案的配合;.framework檔案可以直接使用

.a + .h + sourceFile = .framework

建議使用.framework

靜態庫的除錯

因為靜態庫只提供了一些標頭檔案,實現程式碼都是經過二進位制化的,如果我們想對靜態庫進行斷點除錯如何做呢?

我們可以建立複合專案來對靜態庫進行除錯:

建立一個測試工程,選中TARGETS,點選下面的➕按鈕,新增一個靜態庫,如下圖所示

1154433-03cb8b3cf56eb562.png

複合專案

設定主工程檔案和靜態庫的依賴關係。

1154433-1acf8f288a2eed23.png

新增依賴關係

經過以上兩步後我們就可以對要製作的靜態庫檔案進行除錯開發了,是不是很爽,至於製作靜態庫就和上面的步驟一樣了。

Bundle封裝XIB、圖片等資原始檔

建立Bundle檔案

因為我是新建了一個複合測試工程,所以在這裡為了方便我們直接在複合測試工程中新增了一個子工程,因為iOS框架中沒有bundle,要選中macOS框架找到bundle,如下圖為我們的Bundle命名為STWNetPay

1154433-ea3608c5226e9cbf.jpg

image

Bundle的本質其實是一個目錄,我們完全可以建立一個資料夾,然後把需要封裝的資原始檔拷貝到該目錄下,然後將字尾名為改成bundle即可,但是如果我們想要將XIB檔案封裝到Bundle檔案中的話,這種方法就不可以了. 考慮到xib是文字檔案,編譯後要被序列化為二進位制的nib檔案,使用時將nib檔案反序列化,就可以正常顯示介面了。而bundle本身是靜態的,其內部的資源包不參與專案的編譯,所以,此處必須建立工程把xib序列化為二進位制的nib,否則的話,直接建立資料夾後改名就可以了。

設定Bundle

1.建立好之後我們發現裡面只有一個Info.plist檔案,經測試該檔案不能刪掉,如果刪掉之後編譯Bundle檔案會報錯:bundle format unrecognized, invalid, or unsuitable

2.iOS Deployment Target改為你支援的最低版本

1154433-92a6ec779d0a218f.jpg

image

3.設定base SDK 為latest iOS

1154433-6ecc7a7324b27568.jpg

image

4.COMBINE_HIDPI_IMAGES設定為NO

因為iOS建立Bundle時放入的圖片資源(.png)在預設配置下會被轉為.tiff格式,使用的時候找不到。所以找到bundle的工程,Buld Settings > COMBINE_HIDPI_IMAGES設定為NO

1154433-dd9cf42c42699214.jpg

image

5.Skip Install 設定為NO

1154433-9b76115a062486d5.jpg

image

6."Build Active Architecture Only" 設定為 NO

向Bundle中新增資原始檔

1154433-b72737c2b1fadc5d.jpg

image

一切就緒,按快捷鍵"Command + B"編譯,編譯成功後,我們的Bundle檔案即由紅色變成了黑色,表示編譯成功,這個時候找到我們的Bundle檔案,顯示報內容,如下圖所示:

1154433-23e3c852883418fb.jpg

image

可以看到XIB檔案經過編譯已經變成了nib檔案,這就是我們想要的.

使用bundle裡的資原始檔

將編譯好之後的Bundle檔案拖入我們的靜態庫中,因為是要在靜態庫中使用Bundle檔案,而不是在MainBundle中,所以我們需要先找到我們Bundle所在的路徑才能使用它裡面的資源.

建立一個管理Bundle路徑的類BundleTools,專門用來獲取我們Bundle所在的路徑:

#import<Foundation/Foundation.h>#define BUNDLE_NAME @"STWNetPay"@interfaceBundleTools:NSObject+ (NSString*)getBundlePath: (NSString*) assetName;+ (NSBundle*)getBundle;@end

#import"BundleTools.h"@implementationBundleTools+ (NSBundle*)getBundle{NSURL*url=  [[NSBundlebundleForClass:[selfclass]] URLForResource:BUNDLE_NAME withExtension:@"bundle"];NSBundle*bundle = [NSBundlebundleWithURL:url];returnbundle;}+ (NSString*)getBundlePath: (NSString*) assetName{NSBundle*myBundle = [BundleTools getBundle];if(myBundle && assetName) {return[[myBundle resourcePath] stringByAppendingPathComponent: assetName];    }returnnil;}@end

使用圖片

UIImage*image = [UIImageimageWithContentsOfFile: [BundleTools getBundlePath:@"nav_back"]];

使用XIB

控制器是XIB的情況,此時應該在控制器中重寫Init方法

- (instancetype)init{NSBundle*myBundle = [BundleTools getBundle];//從bundle中獲取介面檔案self= [superinitWithNibName: [NSStringstringWithUTF8String: object_getClassName(self)] bundle: myBundle];if(self) {// Custom initialization}returnself;}

否則

NSBundle*myBundle = [BundleTools getBundle];//從bundle中獲取介面檔案view = [myBundle loadNibNamed:@""owner:niloptions:nil].firstObject;

Swift打包動態庫

因為Swift專案是不支援靜態庫的,必須使用動態庫。所以我們在新建一個靜態庫的時候,一定要選擇Cocoa Touch Framework

1154433-48b6cd0897d37713.png

建立動態庫

如果我們在專案中將target -> Build Settings-> 搜尋 Mach-o Type ;改為Static Library

1154433-6820bc83a40a648d.png

靜態庫

Command+B編譯,系統會報一個錯,提示我們Swift is not supported for static libraries.如下圖:

1154433-b12704faf77de25c.png

靜態庫報錯

所以,如果在Swift專案中,我們必須使用動態庫;

因為Swift是沒有標頭檔案這個說法的,所以在Build Phases中我們也不用設定暴露標頭檔案了

1154433-40e0388a89d35693.png

11.png

確定支援模擬器或者真機中的所有架構

1154433-b909d9a3fbefc285.png

編譯架構

最重要的,因為swift沒有標頭檔案,所以如果我們想讓別人用到我們的方法,就用Public修飾符修飾。

1154433-5471fab173ab4d11.png

暴露方法

這樣生成的動態庫中就會將我們的Swift方法轉換成了OC的方法

SWIFT_CLASS("_TtC10zpSwiftLib4Tool")@interfaceTool:NSObject+ (void)Log;- (nonnullinstancetype)init OBJC_DESIGNATED_INITIALIZER;@end

這樣將生成的動態庫拖入到我們的測試工程中就可以成功的使用了,但是和靜態庫有一點不同的是,我們需要在測試工程中General->Embedded Binaries中匯入我們這個動態庫即可。

連結:https://www.jianshu.com/p/f14553494d88

相關文章