Swift之深入解析“对象”的底层原理
生活随笔
收集整理的這篇文章主要介紹了
Swift之深入解析“对象”的底层原理
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、Swift 編譯簡介
- Swift 的編譯環境配置和編譯流程,請參考我之前的博客:Swift之源碼編譯的環境搭建和編譯流程;
- 新建一個 Swift 工程,在 main.swift 中創建一個 YDWTeacher 類,并通過默認的初始化器,創建一個實例對象并賦值給 t,如下:
- 然后在終端中查看抽象語法樹:swiftc -dump-ast main.swift,如下:
- 接下來,要研究的是這個初始化器到底做了一個什么樣的操作?因此引入 SIL (Swift intermediate language);
- iOS 的開發語言,不管是 OC 還是 Swift,底層都是通過 LLVM 編譯的,生成 .o 可執行文件,如下所示:
- 不難看出:
- OC 中通過 clang 編譯器,編譯成 IR,然后再生成可執行文件.o(即機器碼);
- swift 中通過 swiftc 編譯器,編譯成 IR,然后再生成可執行文件;
- 再來看一下:一個 Swift 文件的編譯過程經歷哪些步驟:
- 下面是 Swift 中的編譯流程,其中 SIL(Swift Intermediate Language),是 Swift 編譯過程中的中間代碼,主要用于進一步分析和優化 Swift 代碼。如下圖所示,SIL 位于在 AST 和 LLVM IR 之間:
- Swift 與 OC 的區別在于 Swift 生成了高級的 SIL;Swift 在編譯的過程中使用的前端編譯器是 Swiftc,和我們之前的 OC 中使用的 clang 是有所區別的。
- 通過 swiftc -h 終端命令,查看 swiftc 能做什么:
- 分析說明:
- -dump-ast 語法和類型檢查,打印 AST 語法樹
- -dump-parse 語法檢查,打印 AST 語法樹
- -dump-pcm 轉儲有關預編譯 Clang 模塊的調試信息
- -dump-scope-maps expanded-or-list-of-line:column
Parse and type-check input file(s) and dump the scope map(s) - -dump-type-info Output YAML dump of fixed-size types from all imported modules
- -dump-type-refinement-contexts
Type-check input file(s) and dump type refinement contexts(s) - -emit-assembly Emit assembly file(s) (-S)
- -emit-bc 輸出一個 LLVM 的 BC 文件
- -emit-executable 輸出一個可執行文件
- -emit-imported-modules 展示導入的模塊列表
- -emit-ir 展示 IR 中間代碼
- -emit-library 輸出一個 dylib 動態庫
- -emit-object 輸出一個 .o 機器文件
- -emit-pcm Emit a precompiled Clang module from a module map
- -emit-sibgen 輸出一個 .sib 的原始 SIL 文件
- -emit-sib 輸出一個 .sib 的標準 SIL 文件
- -emit-silgen 展示原始 SIL 文件
- -emit-sil 展示標準的 SIL 文件
- -index-file 為源文件生成索引數據
- -parse 解析文件
- -print-ast 解析文件并打印(漂亮/簡潔的)語法樹
- -resolve-imports 解析 import 導入的文件
- -typecheck 檢查文件類型
二、SIL
① 什么是 SIL 分析?
- SIL 依賴于 swift 的類型系統和聲明,所以 SIL 語法是 swift 的延伸。一個 sil 文件是一個增加了 SIL 定義的 swift 源文件;
- SIL 文件中沒有隱式 import,如果使用 swift 或者 Buildin 標準組件的話必須明確的引入;
- SIL 函數由一個或多個 block 組成,一個 block 是一個指令的線性序列,每個 block 中的最后一條指令將控制轉移到另一個 block,或從函數返回。
- 如果想要對 SIL 的內容進行詳細地探索,請參考:2015 LLVM Developers’ Meeting。
② SIL 分析 mian 函數
- 查看抽象語法樹之后,繼續在終端中調用 swiftc -emit-sil main.swift >> ./main.sil && code main.sil 命令,生成 main.sil 文件;
- 用 VSCode 打開 SIL 文件:
- 分析:
- @main 這?標識當前 main.swift 的??函數,SIL 中的標識符名稱以 @ 作為前綴;
- %0, %1… 在 SIL 也叫做寄存器,這?可以理解為?常開發中的常量,?旦賦值之后就不可以再修改,如果 SIL 中還要繼續使?,那么就不斷的累加數值。 同時這?所說的寄存器是虛擬的,最終運?到機器上,會使?真的寄存器;
- alloc_gobal:創建?個全局變量;
- global_addr: 拿到全局變量的地址,賦值給 %3;
- metatype 拿到 YDWTeacher 的 Metadata 賦值給 %4 將 __allocating_init 的函數地址賦值給 %5;
- __apply 調? __allocating_init , 并把返回值給 %6;
- 將 %6 的值存儲到 %3(也就是剛剛創建的全局變量的地址);
- 構建 Int , 并 return;
- 注意:code 命令是在 .zshrc 中做了如下配置,可以在終端中指定軟件打開相應文件:
- 從 SIL 文件中,可以看出,代碼是經過混淆的,可以通過以下命令還原,以s4main1tAA10YDWTeacherCvp 為例:xcrun swift-demangle s4main1tAA10YDWTeacherCvp,結果如下:
- 在 SIL 文件中搜索 s4main10YDWTeacherCACycfC,其內部實現主要是分配內存+初始化變量:
- allocing_ref:創建一個 YDWTeacher 的實例對象,當前實例對象的引用計數為1;
- 調用init方法;
- SIL語言對于Swift源碼的分析是非常重要的,關于其更多的語法信息,可以參考:Swift Intermediate Language (SIL)。
三、符號斷點調試
- 在我們的 TestSwift 工程中設置“__allocating_init”符號斷點;
- 然后執行,可以看到:內部調用的是swift_allocObject;
四、源碼分析
- 在 VSCode 中的REPL(命令交互行,類似于python的,可以在這里編寫代碼)中編寫如下代碼(也可以拷貝),并搜索 *_swift_allocObject 函數加一個斷點,如下所示:
- 然后初始化一個實例對象t,回車:
- 這里的 Local 中可以看出:requiredSize 是內存大小,requiredAlignmentMask 是內存對齊方式;requiredAlignmentMask 是 swift 中的字節對齊方式,這個和 OC 中是一樣的,必須是8的倍數,不足的會自動補齊,目的是以空間換時間,來提高內存操作效率;
- swift_allocObject 的源碼如下,主要分為:
- 通過 swift_slowAlloc 分配內存,并進行內存字節對齊;
- 通過 new + HeapObject + metadata 初始化一個實例對象;
- 函數的返回值是 HeapObject 類型,所以當前對象的內存結構就是 HeapObject 的內存結構;
- 進入 swift_slowAlloc 函數,其內部主要是通過 malloc 在堆中分配 size 大小的內存空間,并返回內存地址,主要是用于存儲實例變量:
- 進入 HeapObject 初始化方法,需要兩個參數:metadata、refCounts:
- 分析:
- 其中 metadata 類型是 HeapMetadata,是一個指針類型,占8字節;
- refCounts(引用計數,類型是 InlineRefCounts,而 InlineRefCounts 是一個類 RefCounts 的別名,占8個字節),swift 采用 arc 引用計數;
五、總結
- 對于實例對象 t 來說,其本質是一個 HeapObject 結構體,默認 16 字節內存大小(metadata 8字節 + refCounts 8字節),與 OC 的對比如下:
- OC 中實例對象的本質是結構體,是以 objc_object 為模板繼承的,其中有一個 isa 指針,占 8 字節;
- Swift 中實例對象,默認的比 OC 中多了一個。refCounted 引用計數大小,默認屬性占 16 字節;
- Swift 中對象的內存分配流程是:_allocating_init --> swift_allocObject --> _swift_allocObject --> swift_slowAlloc --> malloc;
- init 在其中的職責就是初始化變量,這點與 OC 中是一致的。
總結
以上是生活随笔為你收集整理的Swift之深入解析“对象”的底层原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS之CocoaPods二进制化的实现
- 下一篇: Swift之深入解析“类”的底层原理