iOS開發(fā)進階八:Module(模塊)

module(模塊):最小的代碼單元

一個module是機器代碼和數(shù)據(jù)的最小單位,可以獨立于其他代碼單位進行鏈接宣增。

這句話的的解釋:

通常,module是通過編譯單個源文件生成的目標文件蚁廓。例如:當前的test.m被編譯成目標文件test.o時盈简,當前的目標文件就代表了一個module。

但是携取,有一個問題攒钳,Module在調用的時候會產(chǎn)生開銷,比如我們在使用一個靜態(tài)庫的時候雷滋,可以這樣使用

@import TestStaticFramework;

這個靜態(tài)庫中可能包含了許多的.o文件不撑。豈不是要導入很多的Module文兢。并不需要。在靜態(tài)鏈接的時候焕檬,也就是靜態(tài)庫鏈接到主項目或者動態(tài)庫時姆坚,最后生成可執(zhí)行文件或者動態(tài)庫時。靜態(tài)鏈接器可以把多個Module鏈接優(yōu)化成一個实愚,來減少本來多個Module直接調用的問題兼呵。

  • 問題一:我們經(jīng)常講.m的編譯,那.h是怎么編譯的腊敲?
  • 問題二:Module在調用的時候會產(chǎn)生開銷击喂,比如我們在使用一個靜態(tài)庫的時候。這種開銷是怎么引起的碰辅?系統(tǒng)是怎么優(yōu)化這種開銷懂昂?
  • 問題三:通常我們在項目中使用#import 導入AFN,實際上需要導入很多個頭文件没宾,這其中產(chǎn)生了什么問題凌彬?當我們在代碼中導入一個庫或者引入其它頭文件的時候,發(fā)生了什么事情循衰?
  • 問題四:ModuleMap是什么铲敛?

#include#import差異

案例:現(xiàn)存在3個頭文件:A.hB.h会钝、C.h伐蒋。在B.h中導入頭文件A.h,在C.h中導入頭文件B.h顽素,在C.c中導入C.h咽弦。

編譯時,編譯器按順序編譯這些文件胁出,

#include導入方式:

  • 先到A時型型,A中沒有導入其它文件,只需編譯A全蝶。
  • 到B時闹蒜,因為B中導入了A,A又要編譯一次抑淫,需要編譯A和B绷落。
  • 到C時,因為C導入了B始苇,所以需要編譯B砌烁,編譯B時,因為B導入了A,A也要再次編譯函喉。即A避归、B、C都要編譯一次管呵。

#import導入方式:

  • 頭文件會被預先編譯成二進制文件梳毙,并且每個頭文件只會被編譯一次。此時無論有多少文件導入頭文件捐下,都不會被重復編譯账锹。

驗證#include導入方式

可以用指令看看編譯器在預處理階段幫我們做了哪些事情:

clang -E use.c
Clang預處理中頭文件處理過程.png

無論文件中是#import還是#includeclang預編譯出來的結果是一樣的坷襟,意思就是執(zhí)行的流程是一樣的奸柬。但是在具體到每個步驟的時候,存在差異:#import導入時啤握,直接使用預先編譯好的二進制文件鸟缕。

每次包含標頭時,編譯器都必須可傳遞地預處理和解析該標頭及其包含的每個標頭中的文本排抬。必須對應用程序中的每個翻譯單元重復此過程,這涉及大量的冗余工作授段。

#include偽指令被預處理程序視為文本包含蹲蒲,因此在包含時必須接受任何活動的宏定義。如果任何活動宏定義碰巧與庫中的名稱沖突侵贵,則可能會破壞庫API或導致庫頭本身的編譯失敗届搁。

此外,導入模塊時將自動提供使用該模塊所需的任何鏈接器標志

每個模塊都被解析為一個獨立的實體窍育,因此它具有一致的預處理器環(huán)境卡睦。

此外,在遇到導入聲明時漱抓,當前的預處理器定義將被忽略表锻,

@import上面的聲明導入std模塊的全部內容(其中將包含例如整個C或C ++標準庫),并在當前翻譯單元中提供其API乞娄。要僅導入模塊的一部分瞬逊,可以使用點語法來特定特定的子模塊

模塊會自動將#include指令轉換為相應的模塊導入

關于開銷的問題:

如果只編譯一個C.c文件,A仪或、B确镊、C這3個頭文件都需要編譯一次,兩種導入方式無差異范删。但是真正的項目中蕾域,依賴關系通常都很復雜,使用import做到每個文件只編譯一次到旦,就可以節(jié)省開銷旨巷。

在具有N個翻譯單元和每個翻譯單元中包含M個標頭的項目中廓块,即使M個標頭中的大多數(shù)在多個翻譯單元之間共享,編譯器仍在執(zhí)行M x N個工作契沫。

std.io模塊僅編譯一次带猴,并且將模塊導入轉換單元是恒定時間操作(與模塊系統(tǒng)無關)。因此懈万,每個軟件庫的API僅解析一次拴清,從而將M x N編譯問題減少為M + N問題。

用modulemap驗證#import導入方式

參考文檔

https://clang.llvm.org/docs/APINotes.html

https://clang.llvm.org/docs/Modules.html#export-declaration

# -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

新建module.modulemap文件:

準備驗證modulemap的使用.png

module.modulemap用來描述頭文件與module之間映射的關系

  • 定義了名稱為A和B的兩個module
  • module A中口予,定義了header A.h,表示module A和A.h的映射關系
  • module B中涕侈,定義了header B.h沪停,和A同理。export A表示將B.h導入的A.h頭文件重新導出

用clang指令使用fmodules方式生成目標文件:

fmodules方式編譯后生成的pcm二進制文件.png

moduleXcode中是默認開啟的裳涛。如果在Build Settings中木张,將Enable Modules設置為NO,導入頭文件將不能使用@import方式端三。開啟module后舷礼,項目中導入頭文件,無論使用#include郊闯、#import妻献、@import中何種方式,最終都會映射為@import方式团赁。

Cocoapod安裝的AFNetworking文件的modulemap

// 聲明framework的module名稱為AFNetworking
framework module AFNetworking {
  // 導入文件的集合(如果沒有關鍵字header那么umbrella后面需要跟上頭文件的文件夾名稱)
  umbrella header "AFNetworking-umbrella.h"

  export * //把引入的頭文件重新導出育拨。
  module * { export * } //把導入頭文件修飾成子module,并把符號全部導出(第一個通配符*表示子module名稱和父module名稱一致)

// 如果要指定子module的名稱需要使用explicit關鍵字
// eg:
  explicit module NANetworking {
    header "NANetworking.h"
    export *
  }
}

umbrella:雨傘頭欢摄,可以理解為傘柄熬丧。一把雨傘的傘柄下有很多傘骨,umbrella的作用是指定一個目錄剧浸,這個目錄即為傘柄锹引,目錄下所有.h頭文件即為傘骨。

explicit:顯示指明子module名稱唆香。

自定義Module

為什么需要用到自定義Module嫌变?

因為在生成一個自定義庫時,在我們的Framework項目中并沒有幫我們生成ModuleMap文件躬它,它只會在編譯時自動幫我們生成腾啥。這樣就存在一個問題,如果我們想在項目中配置自己的東西,比如說配置一個子Module倘待。這種場景下疮跑,我們就需要寫一個自己的Module文件。

  • 寫好一個Module文件后凸舵,將項目加到對應Framework項目的Tartget中祖娘。
  • 配置設置中的Module Map File,路徑的設置是以SRCRoot為前置路徑的啊奄。
  • 因為系統(tǒng)默認查找module.modulemap文件渐苏。自定義的modulemap文件,無論什么名稱菇夸,在編譯后琼富,都會把文件名稱改成module.modulemap文件名。
  • 子module的導出可以用通配符庄新,也可以一個個單獨指定鞠眉。
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市择诈,隨后出現(xiàn)的幾起案子械蹋,更是在濱河造成了極大的恐慌,老刑警劉巖吭从,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朝蜘,死亡現(xiàn)場離奇詭異,居然都是意外死亡涩金,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門暇仲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來步做,“玉大人,你說我怎么就攤上這事奈附∪龋” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵斥滤,是天一觀的道長将鸵。 經(jīng)常有香客問我,道長佑颇,這世上最難降的妖魔是什么顶掉? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮挑胸,結果婚禮上痒筒,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好簿透,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布移袍。 她就那樣靜靜地躺著,像睡著了一般老充。 火紅的嫁衣襯著肌膚如雪葡盗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天啡浊,我揣著相機與錄音觅够,去河邊找鬼。 笑死虫啥,一個胖子當著我的面吹牛蔚约,可吹牛的內容都是我干的。 我是一名探鬼主播涂籽,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼苹祟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了评雌?” 一聲冷哼從身側響起树枫,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎景东,沒想到半個月后砂轻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡斤吐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年搔涝,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片和措。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡庄呈,死狀恐怖,靈堂內的尸體忽然破棺而出派阱,到底是詐尸還是另有隱情诬留,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布贫母,位于F島的核電站文兑,受9級特大地震影響,放射性物質發(fā)生泄漏腺劣。R本人自食惡果不足惜绿贞,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望誓酒。 院中可真熱鬧樟蠕,春花似錦贮聂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至靡狞,卻和暖如春耻警,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背甸怕。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工甘穿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人梢杭。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓温兼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親武契。 傳聞我的和親對象是個殘疾皇子募判,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容