一、module與modulemap的作用
1、 module
- Module(模塊)-最小的代碼單元。
一個Module是機(jī)器代碼和數(shù)據(jù)的最小單位,可以獨(dú)立于其他代碼單位進(jìn)行鏈接。通常,Module是通過編譯單個源文件生成的目標(biāo)文件。例如,當(dāng)前的test.m被編譯成目標(biāo)文件test.o時,當(dāng)前的目標(biāo)文件就代表了一個Module。但是,有一個問題,Module在調(diào)用的時候會產(chǎn)生開銷,比如我們在使用一個靜態(tài)庫的時候,可以這樣使用。
// -fmodules:允許使用module語言來表示頭文件
// -fmodule-map-file:module map的路徑。如不指明默認(rèn)module.modulemap
// -fmodules-cache-path:編譯后的module緩存路徑
clang -fmodules -fmodule-map-file=Cat.modulemap -fmodules-cache-path=../prebuilt -c use.c -o use.o
2、modulemap
- modulemap定義:用來描述頭文件與module之間映射的關(guān)系的文件。
// AFNetworking的module.modulemap文件
// framework module 名稱 AFNetworking
framework module AFNetworking {
// umbrella <目錄> 傘柄 <目錄>/.h
// AFNetworking-umbrella.h 傘柄 AFNetworking-umbrella.h/.h 傘骨
umbrella header "AFNetworking-umbrella.h"
// 重新導(dǎo)出
export *
// module: 子module*
module * { export * }
}
/*
module:定義一個module
export:導(dǎo)出當(dāng)前代表的頭文件使用的頭文件
export * :匹配目錄下所有的頭文件
module * :目錄下所有的頭文件都當(dāng)作一個子module
explicit :顯式聲明一個子module的名稱
*/
3、module實操
默認(rèn)開啟module之后,在引入頭文件時使用include""、 import<>、@import ;這三種寫法,最終都會被轉(zhuǎn)化成@import。
@import TestStaticFramework;
這個靜態(tài)庫中可能包含了許多的.o文件。豈不是要導(dǎo)入很多的Module。并不需要。在靜態(tài)鏈接的時候,也就是靜態(tài)庫鏈接到主項目或者動態(tài)庫時,最后生成可執(zhí)行文件或者動態(tài)庫時。靜態(tài)鏈接器可以把多個Module鏈接優(yōu)化成一個,來減少本來多個Module直接調(diào)用的問題。每次包含標(biāo)頭時,編譯器都必須可傳遞地預(yù)處理和解析該標(biāo)頭及其包含的每個標(biāo)頭中的文本。必須對應(yīng)用程序中的每個翻譯單元重復(fù)此過程,這涉及大量的冗余工作。
在具有N個翻譯單元和每個翻譯單元中包含M個標(biāo)頭的項目中,即使M個標(biāo)頭中的大多數(shù)在多個翻譯單元之間共享,編譯器仍在執(zhí)行M x N個工作。
include偽指令被預(yù)處理程序視為文本包含,因此在包含時必須接受任何活動的宏定義。如果任何活動宏定義碰巧與庫中的名稱沖突,則可能會破壞庫API或?qū)е聨祛^本身的編譯失敗。
此外,導(dǎo)入模塊時將自動提供使用該模塊所需的任何鏈接器標(biāo)志
std.io模塊僅編譯一次,并且將模塊導(dǎo)入轉(zhuǎn)換單元是恒定時間操作(與模塊系統(tǒng)無關(guān))。因此,每個軟件庫的API僅解析一次,從而將M x N編譯問題減少為M + N問題。
每個模塊都被解析為一個獨(dú)立的實體,因此它具有一致的預(yù)處理器環(huán)境。
此外,在遇到導(dǎo)入聲明時,當(dāng)前的預(yù)處理器定義將被忽略,
@import上面的聲明導(dǎo)入std模塊的全部內(nèi)容(其中將包含例如整個C或C ++標(biāo)準(zhǔn)庫),并在當(dāng)前翻譯單元中提供其API。要僅導(dǎo)入模塊的一部分,可以使用點語法來特定特定的子模塊
模塊會自動將#include指令轉(zhuǎn)換為相應(yīng)的模塊導(dǎo)入
APINotes官方鏈接:https://clang.llvm.org/docs/APINotes.html
Modules官方鏈接:https://clang.llvm.org/docs/Modules.html#export-declaration
二、Swift庫使用OC代碼
Swift庫使用OC代碼:不能使用橋接文件
以下方式就可以使用了:1. oc的頭文件放到modulemap下;2. oc的頭文件放到私有的modulemap下; 3. oc語言特性 協(xié)議 -》swift-〉協(xié)議(暴露) -》 OC
- 創(chuàng)建是私有的modulemap文件命令中必須含有 private 比如:LGSwiftFramework.private.modulemap。并且在該文件內(nèi)部LGSwiftFramework后面必須加上_Private,其中 P 必須大寫。這些都是規(guī)則。
- private.modulemap文件并不是真正意義上的讓外部文件不能使用期私有的module,而是僅僅做了一個標(biāo)識,來區(qū)分與.modulemap文件的不同而已。
三、Swift頭文件
- 在 Xcode 9 之后,Swift 開始?持靜態(tài)庫。
Swift 沒有頭?件的概念,那么我們外界要使?Swift中?public修飾的類和函數(shù)怎么辦?
Swift庫中引?了?個全新的?件.swiftmodule。
.swiftmodule 包含序列化過的 AST(抽象語法樹,Abstract Syntax Tree),也包含 SIL(Swift 中間語?,Swift Intermediate Language)。
四、Swift靜態(tài)庫合并
cp -Rv -- "${BUILT_PRODUCTS_DIR}/" "${SOURCE_ROOT}/../Products"
復(fù)制命令:cp(復(fù)制) -R(單獨(dú)復(fù)制文件)-v(使cp冗長,顯示文件的原樣)
$(BUILT_PRODUCTS_DIR):build成功后的,最終產(chǎn)品路徑可以在Build Settings參數(shù)的Per-configuration Build Products Path項里設(shè)置
$(TARGET_NAME):目標(biāo)工程名稱
$(SRCROOT):工程文件(比如Nuno.xcodeproj)的路徑
$(CURRENT_PROJECT_VERSION):當(dāng)前工程版本號
${SOURCE_ROOT}:項目根目錄(如果和項目根目錄平級或更高,就$(SOURCE_ROOT)/../../IBAForms/headers)-
libtool -staticlibtool -static LGSwiftA LGSwiftB -o libLGSwiftC.a -o libLGSwiftC.a
將 LGSwiftA 和 LGSwiftB 合并為libLGSwiftC.a文件
報出的警告:合并的內(nèi)容中LGSwiftTeacher.o文件重復(fù),這里只是報警告,不會報錯。libtool 合并靜態(tài)庫本身,合并時會提示存在相同的文件,且不進(jìn)行合并。
警告 -
ar -t libLGSwiftC.a 列出libLGSwiftC.a文件里面包含的文件。
列出靜態(tài)庫文件內(nèi)容 -
合并 Swift 靜態(tài)庫難題:靜態(tài)庫合并后還有 .swiftmodule文件沒有合并,所以將用到的頭文件和Swift頭文件和modulemap文件通過目錄的形式放到一起。如下圖:
header
swiftmodule Swift 合并的靜態(tài)庫文件應(yīng)用,進(jìn)行配置
OC要用合并的靜態(tài)庫:clang: other c flags :-fmodule-map-file <modulemap path>
Swift要用合并的靜態(tài)庫 : SwiftC :other swift flags 顯式告訴SwiftC <modulemap dir>
HEADER_SEARCH_PATHS = $(inherited) "${SRCROOT}/LGSwiftC/Public/LGSwiftA.framework/Headers" "${SRCROOT}/LGSwiftC/Public/LGSwiftB.framework/Headers"
// OTHER_CFLAGS:傳遞給用來編譯C或者OC的編譯器,當(dāng)前就是clang
OTHER_CFLAGS="-fmodule-map-file=${SRCROOT}/LGSwiftC/Public/LGSwiftA.framework/module.modulemap" "-fmodule-map-file=${SRCROOT}/LGSwiftC/Public/LGSwiftB.framework/module.modulemap"
// SWIFT_INCLUDE_PATHS: 傳遞給SwiftC編譯器,告訴他去下面的路徑中查找module.file
SWIFT_INCLUDE_PATHS="${SRCROOT}/LGSwiftC/Public/LGSwiftB.framework" "${SRCROOT}/LGSwiftC/Public/LGSwiftA.framework"
五、Swift與OC相互映射
- OC映射到Swift方式:1. 宏;2. <工程名稱>.apinotes。
- 宏配置的缺點:如果一個 SDK 使用 OC 來寫的,現(xiàn)在需要適配 Swift。這樣就需要給每一個方法或?qū)傩蕴砑雍陙磉m配,這樣就會導(dǎo)致有大量工作要做,費(fèi)時費(fèi)力。并且要修改原有代碼。
- .apinotes:文件以.apinotes結(jié)尾,且該文件一定要放在 SDK 的目錄里。該文件是采用yaml格式書寫。官方地址:https://clang.llvm.org/docs/APINotes.html
---
Name: OCFramework
Classes:
- Name: LGToSwift
SwiftName: ToSwift
Methods:
- Selector: "changeTeacherName:"
Parameters:
- Position: 0
Nullability: O
MethodKind: Instance
SwiftPrivate: true
Availability: nonswift
AvailabilityMsg: "這個不能用"
- Selector: "initWithName:"
MethodKind: Instance
DesignatedInit: true



