modulemap的使用方法

2023-07-19 21:00:46
modulemap的作用
 
modulemap 檔案是用來解決 C,Object-C,C++ 程式碼在 Swift 專案中整合的問題的。
在 Swift 專案中,如果需要使用 C,Object-C 或者 C++ 程式碼,需要將相應的標頭檔案和原始檔匯入到專案中,並且需要手動管理它們之間的依賴關係。導致專案結構複雜,管理困難。
為了解決這個問題,可以使用 modulemap 檔案來定義模組,將 C,Object-C 或者 C++ 程式碼組合成一個模組,然後在 Swift 專案中直接匯入該模組。
檔案中的說明:
As Bridging-Header can help us in App Target and App Test Target, not in static library or dynamic libraries to use the Objective C / C APIs into Swift classes, modulemap can help us here.
 
在Swift專案中使用Objective C / C APIs的方式
Swift使用Objective C / C原始碼時:在App工程中OC檔案可以通過放置在Bridging-Header檔案中讓Swift其他檔案參照。
Swift使用Objective C / C的靜態庫,動態庫時:在framework靜態庫,動態庫中OC檔案可以通過給framework新增module.modulemap讓Swift通過「import xxx模組」方式對檔案進行參照的。

Module匯入方式優點
 
傳統.h檔案匯入的問題
對於基於C語言而來的其他語言,在匯入時,都是匯入.h檔案,開發者目測很難區分到底匯入的是C,C++,還是OC
編譯效能問題
編譯器在預編譯階段碰到import xx.h後,會將xx檔案複製,替換到xx.h這個位置。如果一個.h檔案中包含了多個其他.h檔案,其他.h檔案中又相互包含時,則會出現相同的程式碼多次被替換。
另外模組多次匯入時,還易出現宏定義替換不完全,錯誤替換的問題(如:當前檔案有方法A,它包含的檔案中存在了宏定義A,那改如果替換呢)
module匯入方式
每個模組都是一個完全隔離的個體。
當模組第一次被import時,編譯器會根據modulemap把它裡面的模組編譯成預編譯模組(Precompiled Module)pcm檔案,並將其在本地快取,裡面包含了模組的所有API資訊。
當第二個模組被import匯入時,編譯器會直接找這個模組被編譯後的快取二進位制檔案。提升了編譯效率。


Module模組的使用方式
普通匯入方式:
#import <MyModule/MyModule-Swift.h>
模組匯入語法: 
@import MyModule;
Swift匯入模組的方式
import MyModule
使用模組匯入方式就需要framework下包含module.modulemap檔案,modulemap指明瞭framework中的標頭檔案的邏輯結構和如何對映成模組。
在使用 MyModule 模組時,就可以直接匯入MyModule模組檔案,而不需要手動逐個新增裡面的子模組。

Module模組語法
//給Swift專案用的
framework module MyModule {
  umbrella header "MyModule.h"

  // headers.h 和 module.modulemap  必須在同一group下,否則需要設定 `header "/??/headers.h"`
  header "headers.h"

  requires objc
  export *  //將子模組都匯出到主模組
  module * { export * } //將當前目錄下所有的標頭檔案(包括umbrella header中包含的每個標頭檔案和其他header "headers.h"標頭檔案)都匯出成子模組
}

//給OC專案用的,需要支援objc語言環境
module MyModule.Swift {
  header "MyModule-Swift.h"
  requires objc
}
module * { export * } :將當前目錄下所有的標頭檔案打包成一個模組,
export * 表示:將其他模組中的所有標頭檔案都匯出到當前模組中
export * 和 module * { export * } 同時使用表示將當前目錄下所有的標頭檔案打包成一個模組,並將其他模組中的所有標頭檔案也匯出到當前模組中。
header 命令:表示將指定的標頭檔案新增到當前模組中。
umbrella header 命令:表示將指定的標頭檔案視為一個 umbrella header,該標頭檔案中包含了其他多個標頭檔案的介面。這個標頭檔案中包含了其他多個標頭檔案的介面,因此可以使用 export * 命令將所有介面都匯出到當前模組中。
framework module XXXX 定義了一個 framework 語意的模組
requires objc 說明:匯入模組的編譯單元要支援OC語言環境
header "headers.h" 說明:將標頭檔案aa.h對映為模組
 
模組宣告
[framework] module module-id [extern_c] [system] {
    module-member
}
extern_c:表示moduel中的C程式碼可以被C++使用,相當於新增了extern 'C'這個宣告。
 
常見Module目錄結構
Name.framework/
    Modules/module.modulemap    framework 的模組對映
    Headers/                    包含了 framework 中的標頭檔案
    PrivateHeaders/             包含了 framework 中私有的標頭檔案
    Frameworks/                 包含嵌入的其它 framework
    Resources/                  包含額外的資源
    Name                        指向共用庫的符號連結
 
另外
Xcode建立的APP和庫預設都是支援Moduel匯入的,如果不支援可以手動在在 Build Settings 中,Defines Module 的設定為 YES,進行支援。


參考文章:
https://juejin.cn/post/7139724115157450765
https://zhuanlan.zhihu.com/p/602783297
https://www.jianshu.com/p/ce49d8f32f77