iOS制作一個靜態庫

前言

什么是庫

庫是共享程序代碼的方式。庫從本質上來說是一種可執行代碼的二進制格式,可以被載入內存中執行。在開發過程中,一些核心技術或者常用框架,出于安全性和穩定性的考慮,不想被外界知道,所以會把核心代碼打包成庫,只暴露出頭文件以供使用。

庫的分類

庫分靜態庫和動態庫兩種。

靜態庫: 存在.a和.framework兩種形式。.a是一個純二進制文件,.framework中除了有二進制文件之外還有資源文件。.a,要有.h文件以及資源文件配合,.framework文件可以直接使用。總的來說,.a + .h + sourceFile = .framework。所以創建靜態庫最好還是用.framework的形式。

對于靜態庫而言,類似于一個編譯好的.o的集合。在build的過程中,只會參與鏈接的操作,鏈接器會將靜態庫中被使用的部分合并到可執行文件中去,用函數的實際地址來代替函數引用。

動態庫:存在.framework和.tbd兩種形式。

在 iOS8 之前,蘋果不允許第三方框架使用動態方式加載,從 iOS8 開始允許開發者有條件地創建和使用動態框架,這種框架叫做 Cocoa Touch Framework。雖然同樣是動態框架,但是和系統 framework 不同,app 中的使用的 Cocoa Touch Framework 在打包和提交 app 時會被放到 app bundle 中,運行在沙盒里,而不是系統中。也就是說,不同的 app 就算使用了同樣的 framework,但還是會有多份的框架被分別簽名,打包和加載。不過 iOS8 上開放了App Extension功能,可以為一個應用創建插件,這樣主app和插件之間共享動態庫還是可行的。

動態鏈接是使用了Procedure Linkage Table (PLT)。首先這個PLT列出了程序中每一個函數的調用,當程序開始運行,如果動態庫被加載到內存中,PLT會去尋找動態的地址并記錄下來,如果函數被調用過的話,下一次調用就可以通過PLT直接跳轉了。

兩種庫對比

靜態庫,在鏈接時會被完整地復制到可執行文件中,被多次使用就有多份冗余拷貝。好處很明顯,編譯完成之后,庫文件實際上就沒有作用了。目標程序沒有外部依賴,直接就可以運行。當然其缺點也很明顯,就是會使用目標程序的體積增大。

動態庫,與靜態庫相反,動態庫在編譯時并不會被拷貝到目標程序中,目標程序中只會存儲指向動態庫的引用。等到程序運行時,動態庫才會被真正加載進來。系統的動態庫不需要拷貝到目標程序中,自建的動態庫可以由工程內的多個庫共享,因此可以減小目標程序的體積。但是,由于其把靜態鏈接做的事情都搬到運行時來做,程序的啟動會變慢。

進入主題,創建靜態庫

.a靜態庫的創建

創建一個.a靜態庫項目,如下圖所示,我這里使用的是Xcode8.2.1 ,然后點擊Next按鈕,命名庫的名字,我這里的命名是: CreateStaticLibrary

創建成功之后,靜態庫的文件列表如下,在products文件夾內的就是要生成的靜態庫。此刻是紅色的,等到點擊Run按鈕生成成功就會變成黑色。我這里面的 CreateStaticLibrary.h 和 CreateStaticLibrary.m 文件是Xcode8.2.1自己生成的,可以對其進行刪除,其.h文件默認是公開的。

接下來就是 設置靜態庫運行的系統要求

設置完之后,因為我這里是簡單的測試,所以就新建一個繼承于NSObject的新類來拼接字符串,類名為:DealWithString? 在其里面新增方法和實現方法,用來拼接字符串的,如下圖分別在聲明類和實現類中新增所需要的方法

在對應的類中新增對應的方法后,現在可以打包這個靜態庫了。我們首先要選擇公開的頭文件,然后再打包,由于模擬器和真機架構不同,需要選擇該包將運行在哪個環境下,如下圖所示,先選擇公開的頭文件,然后再選擇運行在真機或者模擬器上,然后點擊 Run 按鈕,之后products文件下的.a就會由原來的紅色變成黑色。我這里選擇的是真機。步驟如下截圖

products文件夾下的.a文件由原來的紅色變成黑色就說明打包成功,接下來就是需要我們得到這個靜態庫。選中.a文件 右鍵 Show In Finder,然后看到如下截圖的目錄,查看公開的對應的文件有哪些,從右邊的截圖中箭頭可以看出 CreateStaticLibrary.h和 DealWithString.h是對外的頭文件,這正是我們所需要的。然后我們只要將 include文件和libCreateStaticLibrary.a文件放在自己的項目中既可

最后測試使用靜態庫,我這里是將 include文件(暴露的頭文件)和libCreateStaticLibrary.a文件放在了Library文件里面,如下截圖

最后調用和運行結果如下:

遇到的問題:

如果我添加一個分類,并且公開其頭文件,然后在項目中調用會出現奔潰的現象,直接報改調用的方法找不到,截圖如下

出現原因:由于UNIX的靜態庫實現、linker和Objective-C的動態結構三者之間的問題引起的。Objective-C并不為每個函數定義linker symbol,它只為每個class生成linker symbol。(objc的動態結構)如果你為一個已存在的class創建了category,那么linker并不知道要將原始class實現和category實現聯系起來。這就導致了最終程序中的對象沒法響應category中的方法。

解決辦法: 在使用庫的項目中的Targets中的Build Settings中的Other Linker Flags中添加 -all_load ,然后重新運行。

PS:在真機上打包的庫不能在模擬器上面使用,反之。一旦運行就會出現如下錯誤,編譯時直接不通過。

最后:上面所訴的就是制作Debug(調試)下的真機靜態庫,如果需要制作Release(發布)下的,只需要通過下面截圖將Debug改為Release,運行后生成對應的Release文件,其他步驟和上面一致即可。

制作通用靜態庫可以查看我寫的文章:制作通用靜動態庫

PS:有問題可以通過QQ:1205632644聯系本人!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容