iOS的靜態庫和動態庫
靜態庫簡介
什麼是庫?
- 庫從本質上來說是一種可執行程式碼的二進位制格式,可以被載入記憶體中執行
- 庫就是程式程式碼的集合, 是共享程式程式碼的一種方式
- 庫從廣義上可分為開源庫和閉源庫, 而閉源庫才分為靜態庫和動態庫
- 開源庫: 對外公開原始碼, 能看到具體的程式碼實現, 例如
Github
上面的第三方開源庫都稱之為開源庫 - 閉源庫: 不公開原始碼, 檔案是經過編譯後的二進位制檔案, 看不到具體實現, 例如
.a
檔案和.framework
檔案
- 開源庫: 對外公開原始碼, 能看到具體的程式碼實現, 例如
靜態庫與動態庫
- 靜態庫:連結時完整地拷貝至可執行檔案中,被多次使用就有多份冗餘拷貝
- 靜態庫有兩種存在形式:
.a
和.framework
- 靜態庫有兩種存在形式:
- 動態庫:動態庫則不會複製, 只有一份. 程式執行時動態載入到記憶體; 系統只加載一次, 多個程式共用, 節省記憶體
- 動態庫有兩種存在形式:
.dylib
和.framework
- 需要注意的是: 系統的
.framework
是動態庫,我們自己建立的.framework
是靜態庫
- 動態庫有兩種存在形式:
- 但是專案中如果使用到自己的動態庫, 蘋果是不允許上架!
- 再但是
WWDC2014
上公佈的蘋果對ios8
開放動態載入dylib
的介面 也就是說 開放了動態庫掛載, 但是目前幾乎沒有上架的專案使用
.a
與 .framework
的區別
-
.a
是一個純二進位制檔案不能直接使用, 必須要有.h
檔案才能使用,.h
檔案對外提供介面,.a
檔案是程式碼的具體實現, 即.m
-
.framework
中除了有二進位制檔案之外還有資原始檔, 可以直接使用 - 所以開發中建議使用
.framework
為什麼要使用靜態庫?
- 保護自己的核心程式碼, 國內的企業,掌握有核心技術,同時是又希望更多的程式設計師來使用其技術,因此採用”閉源”的方式開發使用
- 實現iOS專案的元件化, 可以把固定的業務模組編譯成靜態庫
- 開發第三方SDK, 例如: 友盟SDK, 百度地圖SDK….
- 提高專案的編譯速度, 比如專案的元件化, 雖然使用了元件化, 但依然是原始碼, 如果工程龐大, 編譯速度依然非常慢, 但是如果把響應的功能和業務元件編譯成靜態庫, 將會大大提高專案的編譯速度
.a
靜態庫的生成和使用
- 生成
.a
靜態庫, 這裡我們選擇第二個建立 - 靜態庫分真機和模擬機兩種環境
- 在真機環境下編譯出來的是隻適用於真機的靜態庫
- 在模擬機環境下編譯出來的是隻使用模擬機的靜態庫
- 同事使用真機和模擬機的靜態庫, 後面會提到, 暫不贅述
- 在不同的環境下靜態庫支援的架構也是不同的, 模擬器下的靜態庫和真機下的靜態庫不能共用, 不同型號編譯的靜態庫也是不能共用的
- 不同裝置使用的CPU不同,從而使用的CPU架構(指令集)也不同,靜態庫有其支援的CPU架構,若靜態庫在不支援的CPU架構上執行程式就會崩潰
靜態庫的架構
// 1. 模擬器使用的CPU架構: iphone4s - iphone5 : i386 iPhone5s - iPhoneX :x86_64 // 2. 真機使用的CPU架構: iPhone3gs - iPhone4s:armv7 iPhone5 - iPhone5c:armv7s iPhone5s - iPhoneX:armv64
lipo -info 靜態庫名稱
// 執行lipo -info的輸出結果 $ lipo -info libStateLib.a input file libStateLib.a is not a fat file Non-fat file: libStateLib.a is architecture: x86_64
編譯多架構靜態庫
- 在除錯不同機型的過程中, 需要選中每一個模擬器進行編譯, 生成支援對應架構的靜態庫然後合併, 非常蛋疼
- 怎樣一次編譯支援多個架構的的靜態庫?
- 該選項預設是
YES
, 也就是隻編譯當前活躍環境的架構, 設定成No
即可 - 模擬器環境編譯出來的就支援所有的模擬機型號, 真機亦是如此
靜態庫的版本
- 和
iOS
證書一樣分除錯版本(Debug
)和釋出版本Release
- 真機-
Debug
版本 - 真機-
Release
版本 - 模擬器-
Debug
版本 - 模擬器-
Release
版本
除錯版本 Debug
- 真機-
Debug
版本和模擬器-Debug
版本 - 除錯版本的特點
- 除錯版本會包含完整的符號資訊,以方便除錯
- 除錯版本不會對程式碼進行優化
釋出版本 Release
- 真機-
Release
版本和模擬器-Release
版本 - 釋出版本的特點
- 釋出版本不會包含完整的符號資訊,
- 釋出版本的執行程式碼是進行過優化的,
- 釋出版本的大小會比除錯版本的略小,
- 在執行速度方面,釋出版本會更快些,但不意味著會有顯著的提升
生成不同版本的靜態庫
選擇專案 -> Edit Scheme -> Run -> Release/Debug
分別進行編譯, 即可得到不同版本的靜態庫
生成 .a
和 .h
檔案
- 正常情況下, 生成的
.h
檔案是在../include/$(PRODUCT_NAME)
目錄下的,$(PRODUCT_NAME)
指的是專案的名字 - 修改圖中2處的路徑地址, 即可修改
.h
檔案生成的路徑, 填空即為和.a
檔案在同級目錄下
最後編寫好程式碼, command+B
編譯之後, 如圖所示操作即可找到生成的靜態庫
合併靜態庫
.a
// 以下所有方式得到的靜態庫都可以通過lipo -info xx.a方式檢測現有的架構, 注意要在xx.a所在的目錄下 // 1. 合併靜態庫 lipo -create 靜態庫1路徑 靜態庫2路徑 -output 合併後的靜態庫名稱 // 示例 lipo -create /Users/xxx/Debug-iphoneos/libStateLib.a /Users/xxx/Debug-iphonesimulator/libStateLib.a -output hahah.a // 2. 移除某一個架構 lipo -remove 架構名稱 靜態庫絕對路徑 -output 新的靜態庫名字 lipo -remove arm64 /Users/xxx/Build/Products/hahah.a -output ha_arm64.a // 2. 拆分出一個單獨架構的靜態庫 lipo -thin 架構名稱 靜態庫絕對路徑 -output 新的靜態庫名字 lipo -thin arm64 /Users/xxx/Build/Products/hahah.a -output only_arm64.a
.framework
靜態庫
生成 .framework
靜態庫
選擇 framework
建立新工程, 選擇第一個建立 Framework
工程
選擇編譯環境
選擇適配所有真機或者適配所有模擬器(編譯所有架構), Build Settings -> Build Active Architecture Only
選項設為 NO
手動設定靜態庫
剛建立的工程預設建立的是動態庫, 需要手動設定連結型別, Build Settings -> Mach-o Type
設定成 Static Library
靜態庫版本
設定靜態庫的版本, 選擇專案 -> Edit Scheme -> Run -> Release/Debug
分別進行編譯, 即可得到不同版本的靜態庫
新增公開標頭檔案
Target->Build Phases->Headers
中的 Project
中要暴露的標頭檔案拖拽到 Pulic
裡面:
編譯
分別選擇 Generic iOS Device
和任意一個模擬器各編譯一次。編譯完,工程中 Products
資料夾下的 xxx.framework
由紅色變成了黑色,然後 show in finder
,看看生成的檔案
注意事項
- 如果靜態庫中有
category
類,則在使用靜態庫的專案配置中Other Linker Flags
需要新增引數-ObjC
或者-all_load
- 如果建立的
framework
類中使用了.tbd
,則需要在實際專案中匯入.tbd
動態庫。 -
.framework
靜態庫的合併和拆分和.a
靜態庫的方式一樣, 就不在贅述了
bundle
載入資源
- 由於
Xcode
預設在編譯時會把所有的素材檔案匯入到mainBundle
中,可能會讓宿主工程與使用靜態庫的程式衝突。 - 在建立靜態庫的專案中又是難免會用到一些圖片或者
xib
等資源, 類似這些資源在靜態庫中又如何進行管理呢 - 這裡我們就要引入一個
bundle
檔案, 對資源進行管理, 用以存放xib
檔案或者圖片等資源 -
bundle
檔案是是靜態的,不進行編譯的資原始檔, 所以使用時需要找到相應的資源路徑
建立方式
- 把包含資原始檔的資料夾的字尾改為
.bundle
, 這時他就變成一個bundle
檔案 - 或者右鍵顯示包內容就可以把對應的圖片資源等放進檔案中,然後把他丟進工程中就可以使用了
- 呼叫該圖片時, 需要在圖片名前加上
xxx.bundle
字首