iOS:Module

前言:

一個Module是機器代碼和數(shù)據(jù)的最小單元,可以獨立于其他代碼單元進行鏈接,
通常,Module是通過編譯單個源文件生成的目標文件边琉。例如,當前的test.m被編譯成目標文件test.o時记劝,當前的目標文件就代表一個Module
但是变姨,有一個問題,Module在調用的時候會產生開銷厌丑,比如我們在使用一個靜態(tài)庫的時候定欧。

一. Module

1.1 #include 與 #import 區(qū)別
舉例 use.c 中引入 A.h B.h文件,那么編譯use.c的時候怒竿,A.h B.h會一塊進行編譯砍鸠,如果現(xiàn)在 use1.c 中也引入 A.h B.h,那么A.h B.h又會進行一次編譯耕驰,這就是傳統(tǒng)#include的編譯方式
這個時候就體現(xiàn)出了module的作用爷辱,也就是#import,會提前把A.h B.h編譯成二進制文件耍属,需要的時候拿來使用托嚣,再有文件導入時就不會重新編譯。

/* A.h */
#ifdef ENABLE_A
void a() {}
#endif
/* B.h */
#import "A.h"
/* use.c */
#import "B.h"
void use() {
#ifdef ENABLE_A
  a();
#endif
}
/* module.modulemap文件 */
/* modulemap 用來描述頭文件與module之間映射的關系 */
/* A代表A.h  B代表B.h */
module A {
  header "A.h"
}
module B {
  header "B.h"
// 導出厚骗,把B.h引用的頭文件也一并導出示启,也可以用 export *,把所有引用的頭文件一并導出
  export A
}
// 進入Cat文件夾领舰,把use.c文件編譯成use.o文件
# -fmodules:允許使用module語言來表示頭文件
# -fmodule-map-file:module map的路徑夫嗓。如不指明默認module.modulemap
# -fmodules-cache-path:編譯后的module緩存路徑
$ clang  -fmodules -fmodule-map-file=module.modulemap -fmodules-cache-path=../prebuilt -c use.c -o use.o

A.h B.h 編譯之后的產物,這兩個文件就是預編譯好的冲秽,如果其他文件再引入A和B就不用重新編譯了舍咖,如下圖所示

image.png
1.2.查看AFNetworking文件的modulemap文件

framework module AFNetworking { //聲明framework的module名稱為AFNetworking
  //導入文件的集合
  umbrella header "AFNetworking-umbrella.h"
  export * //把引入的頭文件重新導出。
  module * { export * } //把導入頭文件修飾成子module锉桑,并把符號全部導出
}

其他module的操作點這里
開啟module之后無論我們使用#include排霉,#import,@import民轴,最終都會被轉換成@import寫法攻柠,編譯時都會被優(yōu)化成module形式球订,就是同一個文件只會被編譯一次。
1.3 module操作
創(chuàng)建MulitProject.xcworkspace如下圖所示

image.png
image.png
接下來把LGOCFramework瑰钮,LGSwiftFramework庫添加到MulitProject.xcworkspace冒滩,如下圖所示
image.png
手動指定modulemap文件
image.png
我們創(chuàng)建LGOCFramework.modulemap文件文件內容如下

framework module LGOCFramework {
    // umbrella<目錄>
    umbrella header "LGOCFramework.h"
  
    explicit module LGTeacher {
        header "LGTeacher.h"
        export *
    }
    explicit module LGStudent {
        header "LGStudent.h"
        export *
    }
}

編譯成功,并在framework文件中看到module.modulemap文件

二. Swift庫使用OC代碼

在framework中沒有橋接文件浪谴,所以swift代碼沒法直接調用oc开睡,我們要使用module,build setting中配置Module Map File路徑即可使用
自定義LGSwiftFramework.modulemap內容如下

framework module LGSwiftFramework {
    umbrella "Headers"
    export *
}

我們可以在swift代碼中直接使用oc類苟耻,如果我們想在oc類中調用swift代碼篇恒,我們需要通過module指定頭文件#import <項目/項目-Swift.h>
如果我們不想對外暴漏我們的OC類,我們可以創(chuàng)建LGSwiftFramework.private.modulemap梁呈,內容如下

framework module LGSwiftFramework_Private {
    module LGOCStudent {
        header "LGOCStudent.h"
        export *
    }
}

然后在Private Module Map File 中指定路徑婚度。
我們不能通過LGSwiftFramework 的module 來訪問LGOCStudent,但是我們可以通過
LGSwiftFramework_Private來訪問LGOCStudent官卡。
Private Module不是真正意義上的私有,我們可以通過LGSwiftFramework_Private可以訪問醋虏,只是供開發(fā)者區(qū)分
小結
swift調用oc方法有3種方式
第一種:直接配置LGSwiftFramework.modulemap來使用#import <LGSwiftFramework/LGOCStudent>
第二種:配置LGSwiftFramework.private.modulemap來使用 @import LGSwiftFramework_Private.LGOCStudent
第三種:swift與oc約定協(xié)議寻咒,swift調用協(xié)議,協(xié)議再調用oc颈嚼。這里把協(xié)議暴露出來毛秘,達到oc隱藏的目的

三. Swift靜態(tài)庫合并

在Xcode 9.0之后,swift開始支持靜態(tài)庫
swift沒有頭文件的概念阻课,那么我們外界使用swift中的public修飾的類和函數(shù)怎么辦呢叫挟?Swift庫引入了一個全新的文件.swiftModule
.swiftModule包含序列化過的AST(抽象語法樹),也包含SIL(Swift中間語言限煞,Swift Intermediate Language)抹恳。

創(chuàng)建swiftFramework,編譯之后 show in finder署驻,可以看到Modules中多生成一個LGSwiftFramework.swiftmodule目錄奋献,這個目錄下多生成一個x86_64.swiftmodule文件
x86_64-apple-ios-simulator.swiftdoc:這個文檔可以刪除


image.png

創(chuàng)建兩個framework庫,分別為LGSwiftA和LGSwiftB
兩個庫里有一個相同的類

@objc open class LGSwiftTeacher: NSObject {
    public func speek() {
        print("speek!")
    }
    @objc public func walk() {
        print("walk!")
    }
}

使用腳本把兩個靜態(tài)庫編譯后的framework放到products目錄下

cp -Rv -- "${BUILT_PRODUCTS_DIR}/" "${SOURCE_ROOT}/../Products"

進入products目錄下旺上,合并兩個靜態(tài)庫

libtool -static LGSwiftA LGSwiftB -o libLGSwiftC.a
//日志警告瓶蚂,兩個靜態(tài)庫都包含LGSwiftTeacher.o

查看libLGSwiftC.a中的目標文件

$ ar -t libLGSwiftC.a
__.SYMDEF
LGSwiftA_vers.o
LGSwiftTeacher.o
LGSwiftB_vers.o
LGSwiftTeacher.o

我們手動組合LGSwiftC庫,因為文件有沖突宣吱,使用這種目錄結構窃这,可是防止Headers文件內部存在的沖突
image.png

創(chuàng)建新工程LGApp 使用上面的LGSwiftC庫,LGApp工程配置LGApp.Debug.xcconfig文件

HEADER_SEARCH_PATHS = $(inherited) "${SRCROOT}/LGSwiftC/Public/LGSwiftA.framework/Headers" "${SRCROOT}/LGSwiftC/Public/LGSwiftB.framework/Headers"
// OTHER_CFLAGS:傳遞給用來編譯C或者OC的編譯器征候,當前就是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"

并且手動組合LGSwiftC庫如下

image.png
小結
在一個文件類中導入#import <LGSwiftA.h> 就會編譯LGSwiftA靜態(tài)庫以及里面的相同類LGSwiftTeacher洒试,導入#import <LGSwiftB.h>就會編譯LGSwiftB靜態(tài)庫

四. OC映射到Swift方式

讓oc代碼在swift使用中規(guī)范
4.1使用宏
NS_SWIFT_NAME(<#name#>)
NS_REFINED_FOR_SWIFT 在swift方法中, 編譯器會在名稱前加上_
4.2.使用apinotes文件
參考文檔
命名規(guī)則:前面是項目或者sdk名稱朴上,后綴是apinotes
.apinotes文件創(chuàng)建好一定要放到根目錄下

#yaml 類似于 json格式
---
Name: OCFramework
Classes:
- Name: LGToSwift
  SwiftName: ToSwift
  Methods:
  - Selector: "changeTeacherName:"
    Parameters:
    - Position: 0
      Nullability: O
    MethodKind: Instance
    SwiftPrivate: true
    # Availability: nonswift
    #AvailabilityMsg: "prefer 'deinit'"
  - Selector: "initWithName:"
    MethodKind: Instance
    DesignatedInit: true

總結:

module:定義一個module
export:導出當前代表的頭文件使用的頭文件
export * :匹配目錄下所有的頭文件
module * :目錄下所有的頭文件都當作一個子module
explicit : 顯式聲明一個module的名稱

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末垒棋,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子痪宰,更是在濱河造成了極大的恐慌叼架,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件衣撬,死亡現(xiàn)場離奇詭異乖订,居然都是意外死亡,警方通過查閱死者的電腦和手機具练,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門乍构,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人扛点,你說我怎么就攤上這事哥遮。” “怎么了陵究?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵眠饮,是天一觀的道長。 經(jīng)常有香客問我铜邮,道長仪召,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任松蒜,我火速辦了婚禮扔茅,結果婚禮上,老公的妹妹穿的比我還像新娘秸苗。我一直安慰自己召娜,他們只是感情好,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布难述。 她就那樣靜靜地躺著萤晴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胁后。 梳的紋絲不亂的頭發(fā)上店读,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音攀芯,去河邊找鬼屯断。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的殖演。 我是一名探鬼主播氧秘,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼趴久!你這毒婦竟也來了丸相?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤彼棍,失蹤者是張志新(化名)和其女友劉穎灭忠,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體座硕,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡弛作,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了华匾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片映琳。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蜘拉,靈堂內的尸體忽然破棺而出萨西,到底是詐尸還是另有隱情,我是刑警寧澤诸尽,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布原杂,位于F島的核電站,受9級特大地震影響您机,放射性物質發(fā)生泄漏。R本人自食惡果不足惜年局,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一际看、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧矢否,春花似錦仲闽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至验庙,卻和暖如春顶吮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背粪薛。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工悴了, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓湃交,卻偏偏與公主長得像熟空,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子搞莺,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內容