如何持續(xù)的自我提升
最近經(jīng)常遇到有些朋友問題我如何學(xué)習(xí)編譯器等各類底層的知識。
這些問題的背后是很多程序員對自我提升的強烈需求。
今天,筆者會通過分享“2020年最后一天的學(xué)習(xí)完整記錄”的方式解答這個問題。
注意:本文是分享自我提升技巧,所以遇到不懂的技術(shù)名詞,可以直接跳過。
一、初識
“初識是一個被灌輸知識的過程。
當(dāng)我們看博客或者書籍時,都會遇到一些新知識。這就是初識。
今天,筆者從 百度App Objective-C/Swift 組件化混編之路(二)- 工程化 時,就被灌輸了一個”新知識“:module 會供鏈接器使用 。
下面截取部分原文:
“1.2 Module 化 1.2.1 基本概念
module:是一個編譯單元,或構(gòu)建產(chǎn)物,對一個軟件庫的結(jié)構(gòu)化替代封裝,供鏈接器使用(更多介紹請查閱 Clang-Module:https://clang.llvm.org/docs/Modules.html#introduction)
二、思考
“思考 是一個主動消化知識的過程。
思考 的方式有很多:
新知識是否和已有的知識發(fā)生了沖突? 新知識可以和哪些知識串聯(lián)起來? ....
當(dāng)我看到上面的”新知識“時,就會想:
”module 是如何被鏈接器使用呢?“ ”我也看過很多相關(guān)資料,為什么之前看到的資料都沒有提到鏈接器呢?“
三、探索
“探索 是一個手動進行研究的過程。
探索 過程非常依賴我們的思考能力和記憶能力。
下面是筆者對 module 的一些思考:
我具備多少與 module 相關(guān)的知識 可以通過哪些搜索引擎技巧更快的搜索到與 module 相關(guān)的知識 我們是否有方案驗證 module 與 鏈接器 的關(guān)系 ...
每一次思考都需要我們把記憶能力充分調(diào)用:
回憶與 module 相關(guān)的知識 回憶搜索引擎技巧 回憶如何通過 Xcode 創(chuàng)建工程、動態(tài)庫 回憶 APP 構(gòu)建的每一步的命令 ...
通過上面的初步思考,我決定通過創(chuàng)建 Demo 的方式對 module 會供鏈接器使用 進行驗證。
準(zhǔn)備 Demo工程

Demo 工程會有一個名為 Host 的 APP,同時該 App 會依賴名為 ?FrameW 的動態(tài)庫和其它系統(tǒng)庫。
項目的整體架構(gòu)如下圖:

構(gòu)建
通過 xcodebuild 命令,可以對 Host 進行構(gòu)建。
如下,紅框部分是 Xcode 執(zhí)行 鏈接 Host 時,所調(diào)用的命令:

通過仔細分析上面的完整 命令 信息,我們沒有發(fā)現(xiàn)與 module 明顯相關(guān)的參數(shù),所以,我們可以大膽猜測:?module 與鏈接器沒有關(guān)系。
調(diào)試模式
考慮到編譯器可能通過其它方式進行了信息傳遞,所以,我們通過給上述命令添加參數(shù) -v 的方式進行調(diào)試。

很遺憾,新增參數(shù) -v 后,仍然沒有得到有效的信息。但是,我們得到了一個新的知識 ?clang 會調(diào)用 ld 命令執(zhí)行鏈接任務(wù)。
調(diào)試鏈接過程
接著,我們再次嘗試對 ld 命令添加參數(shù) -v -t 的方式進行調(diào)試。
這次的信息量十足:
鏈接依賴的 .o路徑被完整的打印出來了鏈接依賴的 FrameW路徑被打印出來了鏈接依賴的系統(tǒng)庫路徑被打印出來了

module 文件探究
現(xiàn)在局面很清晰了,ld 與 FrameW 的交互是通過上面日志中的 FrameW.framework/FrameW 文件完成的。如果我們能夠證明 FrameW.framework/FrameW ?文件與 module 沒有關(guān)系,就可以證明 ? ”module 與鏈接器沒有關(guān)系“。
下面,我們將依次分析 ?module 文件與 FrameW.framework/FrameW ?文件,并將兩者對比。
“如果對某些細節(jié)比較好奇,可以在公眾號底部留言,我們后續(xù)再分享
module內(nèi)幕相關(guān)的知識。
module 文件特征
首先,我們進行編譯任務(wù)時,會發(fā)現(xiàn)有一個名為 -fmodules-cache-path 的參數(shù),該參數(shù)的值是一個路徑ModuleCache:
?-fmodules-cache-path=/var/folders/4j/jqzrrjzn0nvgm4pyxrqddxnmm530jm/C/org.llvm.clang.xxxx/ModuleCache
通過 cd命令切到該文件夾通過 find命令配合grep找到FrameW-xxx.pcm通過 file命令,得知該文件是Mach-O文件通過 otool -lgrepsort,我們發(fā)現(xiàn)該文件最大的一個section體積是0x000000000000d318通過 0x000000000000d318配合otool -l和grep -B 3,我們知道它屬于 ?__CLANG __clangast部分通過 segedit命令將該部分導(dǎo)出到單獨文件通過 file,我們了解到該文件屬于data通過 xxd和head命令,我們可以得到magic number是CPCH

通過 CPCH和llvm源碼,我們可以判斷這是 ?AST/PCH file magic number

另外,結(jié)合 llvm 源碼的 llvm::Error GlobalModuleIndexBuilder::loadModuleFile 內(nèi)部邏輯,我們可以確認這個就是 動態(tài)庫module 進行編譯后的產(chǎn)物

通常上面的思考,我們可以得到以下結(jié)論:module 通常會被編譯為單獨的 mach-o 文件,該文件主要負責(zé)在 section:__CLANG __clangast 存儲編譯后的 ast 文件。
動態(tài)庫的特征
而 ld 鏈接的動態(tài)庫 FrameW.framework/FrameW 是 Mach-O 64-bit dynamically linked shared library arm64 文件

通過上面兩種文件的特征,我們可以證明 module 與鏈接器沒有關(guān)系。
“考慮到很多情況,我們沒法找到各種命令行工具進行分析,所以,下面介紹一份搜索引擎版本的探索流程
通過搜索引擎查找 “Xcode 教程”
通過 Xcode 教程了解構(gòu)建的完整步驟,并觀察其中的鏈接環(huán)節(jié)
通過 Xcode 的鏈接環(huán)節(jié),我們可以發(fā)現(xiàn)真正執(zhí)行鏈接的二進制文件是
ld通過搜索引擎查找“如何通過二進制文件找到對應(yīng)的源碼”
嘗試編譯鏈接器(遇到問題,可以通過網(wǎng)絡(luò)引擎搜索解決)
運行鏈接器,并分析鏈接器的源碼執(zhí)行路徑
注意:
Xcode 的構(gòu)建流程,筆者在上次分享的 llvm 編譯器高級用法:第三方庫插樁 有過簡單介紹,后面會有更加詳細的文章分享動態(tài)庫、靜態(tài)庫、APP 的構(gòu)建流程。
通過二進制文件獲取源碼,筆者在上次分享的 iOS 崩潰排查技巧:如何獲取系統(tǒng)庫源碼,后面會有一篇升級版本的獲取系統(tǒng)庫源碼的方案。
四、總結(jié)和超越
“總結(jié) 是對整個學(xué)習(xí)成果的強化過程。
通過 ”初識“-”思考“-”探索” 三部曲,我們會學(xué)到很多知識。
不幸的是,人類的大腦很容易遺忘知識。所以,我們需要一些技巧將記憶強化。
總結(jié) 的方法有很多種,其中最高效的方案就是 費曼技巧。
費曼技巧 是一種「以教為學(xué)」的學(xué)習(xí)方式,通過直白淺顯的語言把復(fù)雜深奧的問題和知識傳授給 小孩子 的方式進行學(xué)習(xí)。
但是,費曼技巧 需要一個傾聽者才會有很好的學(xué)習(xí)效果。所以,我個人更加推薦將 學(xué)習(xí)筆記公開。
當(dāng)我們選擇將筆記發(fā)到公眾號或者個人博客時,就會強迫自己將整篇文章的知識理順,避免錯誤、遺漏。同時,也會有更多的同行幫我們 指出問題。
請記住,只有當(dāng)我們把相關(guān)的知識點完整串聯(lián)和記憶后,我們才能真正 超越自己,才能真正的 自我提升。
本文總結(jié)
本文通過一次完整的學(xué)習(xí)經(jīng)歷分享了 “如何自我學(xué)習(xí)/提升”的問題。
整體的思路如下:
初識:被灌輸知識的過程。 思考:主動消化知識的過程。 探索:手動進行研究的過程 總結(jié)和超越:完成自我提升的過程

后記
除了本文分享的知識外, 百度App Objective-C/Swift 組件化混編之路(二)- 工程化 還有很多的知識或者疑惑點值得研究,下面簡單的列舉幾個,歡迎讀者進行補充。
.tbd是文本類型,為什么原作者會認為dynamic_library的擴展名是.tbd“
dynamic_library:動態(tài)庫,Xcode 7 之前擴展名為 .dylib, Xcode 7 后是 .tbd ;目前官方環(huán)境并不允許為 iOS 平臺添加這種類型。 swiftmodule 的依賴會傳遞嗎?有沒有優(yōu)雅的方式解決?
“
4.7 小知識:swiftmodule 的傳遞依賴性
已知:有組件 A 依賴組件 B,組件 B 依賴組件 C 在 Objective-C 中,B 對外暴露的頭文件中引用了 C 的公開頭文件,我們叫組件 B 傳遞依賴 C,結(jié)果就是編譯組件 A 時必須同時能找到組件 B 和組件 C 的頭文件,否則編譯失敗。
然而 Swift 并沒有公開頭文件一說,只要組件 B
import C,導(dǎo)致 swiftmodule 中也明確標(biāo)記了import C,當(dāng)組件 Aimport B時,也同時import C,如果組件 A 找不到組件 C 的 module,那組件 A 將編譯失敗。下面解決報錯的方案是依據(jù)什么原理?在不依賴 Xcode 的情況下,我們該如何解決?
“
6.3 App 鏈接一個 Swift 二進制時報錯?
當(dāng)一個組件或產(chǎn)物需要鏈接其他 Swift 的產(chǎn)物時,比如 App、單測、動態(tài)庫等,需要告訴 Xcode 開啟 Swift 鏈接功能,開啟方法就是添加一個 Swift 文件,否則報錯。
點個在看少個 bug??
