Swift編譯簡(jiǎn)介
我們先來(lái)開(kāi)一段簡(jiǎn)單的代碼:
class SuperMan {
var age:Int = 18
var name:String = "Aaron"
}
let t = SuperMan()
我們創(chuàng)建一個(gè)SuperMan
類肉津,并通過(guò)默認(rèn)的初始化器强胰,創(chuàng)建一個(gè)實(shí)例對(duì)象并賦值給了t
。
那么問(wèn)題來(lái)了:這個(gè)默認(rèn)的初始化器到底做了一個(gè)什么樣的操作妹沙?
這里我們引入SIL(Swift intermediate language)
偶洋,在閱讀SIL
的代碼之前,我們先來(lái)了解一下什么是SIL
首先不管是OC
還是Swift
距糖,后端都是通過(guò)LLVM
進(jìn)行編譯的玄窝,如下圖所示:
可以看到:
OC
通過(guò)clang
編譯器,編譯成IR
肾筐,然后再生成可執(zhí)行文件.o
(這里也就是我們的機(jī)器碼)Swift
則是通過(guò)Swift 編譯器
編譯成IR
哆料,然后再生成可執(zhí)行文件我們?cè)賮?lái)看一下,一個(gè)
swift
文件的編譯過(guò)程都經(jīng)歷了哪些步驟:
swift
在編譯過(guò)程中使用的前端編譯器是swiftc
,和我們之前在OC
中使用的Clang
是有所區(qū)別的吗铐。我們可以通過(guò)swiftc -h
命令揉阎,來(lái)查看swiftc
都能做什么事情
SIL
- 首先進(jìn)入工程所在的文件夾伤疙,然后執(zhí)行
swiftc -emit-sil main.swift | xcrun swift-demangle >> ./main.sil && code main.sil
指令兵睛。對(duì)該指令有疑問(wèn)的同學(xué),可以點(diǎn)擊這里奋渔,鏈接文章的最后,有對(duì)該指令的詳細(xì)講解壮啊。下方代碼是用VSCode
打開(kāi)的main.sil
文件嫉鲸,
// t
sil_global hidden @main.t : main.SuperMan : $SuperMan
// main
// '@main': 標(biāo)識(shí)當(dāng)前main.swift的入口函數(shù)。SIL中的標(biāo)識(shí)符名稱以‘@’作為前綴
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
//'%0歹啼、%1' 在SIL中叫做寄存器玄渗,可以理解為開(kāi)發(fā)中的常量,一旦賦值就不可修改狸眼,如果想繼續(xù)使用藤树,就需要不算的累加數(shù)字(注意:這里的寄存器指的是‘虛擬寄存器’,與匯編指令‘register read %x’讀取的寄存器不同拓萌,匯編指令讀取的是真實(shí)的寄存器)
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
//‘a(chǎn)lloc_global’ 創(chuàng)建一個(gè)‘全局變量’ --> ‘t
alloc_global @main.t : main.SuperMan // id: %2
//‘global_addr’ 獲取全局變量的地址岁钓,并賦值給%3
%3 = global_addr @main.t : main.SuperMan : $*SuperMan // user: %7
//‘metatype’ 獲取‘Superman’ 的 ‘MetaData’ 賦值給 %4
%4 = metatype $@thick SuperMan.Type // user: %6
// function_ref SuperMan.__allocating_init()
//將 ‘__allocating_init()’ 函數(shù)的地址賦值給 %5
%5 = function_ref @main.SuperMan.__allocating_init() -> main.SuperMan : $@convention(method) (@thick SuperMan.Type) -> @owned SuperMan // user: %6
//’apply‘ 調(diào)用 ’__allocating_init()‘ 并將返回值 賦值給 %6
%6 = apply %5(%4) : $@convention(method) (@thick SuperMan.Type) -> @owned SuperMan // user: %7
//’store‘ 將 %6 的值 存儲(chǔ)到 %3,也就是前面提到的全局變量的地址
store %6 to %3 : $*SuperMan // id: %7
//創(chuàng)建一個(gè)整數(shù)文字值微王,類型為Builtin.Int32屡限,該類型必須為內(nèi)置整數(shù)類型。文字值是使用Swift的整數(shù)文字語(yǔ)法指定的炕倘,值為0钧大,使用者%8
%8 = integer_literal $Builtin.Int32, 0 // user: %9
//構(gòu)建結(jié)構(gòu)體
%9 = struct $Int32 (%8 : $Builtin.Int32) // user: %10
return %9 : $Int32 // id: %10
} // end sil function 'main'
- 下面我們結(jié)合匯編進(jìn)行一下斷點(diǎn)調(diào)試。查看一下
SuperMan
的創(chuàng)建流程罩旋,如下圖打斷點(diǎn)
在Dubge
中選擇Always Show Disassembly
接下來(lái)我們會(huì)在匯編代碼中發(fā)現(xiàn)一個(gè)SuperMan.__allocating_init()
我們使用si
指令跟進(jìn)去會(huì)發(fā)現(xiàn)該方法的內(nèi)部會(huì)調(diào)用SuperMan.init()
函數(shù)拓型,并且會(huì)先調(diào)用swift_allocObject
函數(shù)。
源碼調(diào)試
根據(jù)上面的分析瘸恼,接下來(lái)我們通過(guò)swift_allocObject
來(lái)探索swift中對(duì)象的創(chuàng)建過(guò)程,通過(guò)搜索swift_allocObject
我們找到了該函數(shù)的實(shí)現(xiàn)
-
接下來(lái)我們來(lái)進(jìn)行斷點(diǎn)調(diào)試
- 可以看到左側(cè)local有詳細(xì)的信息
- 其中
requiredSize
是分配的實(shí)際內(nèi)存大小册养,為40东帅。其中metadata:8, refCounts:8, age:8, name:16。 -
requiredAligmentMask
是swift中的字節(jié)對(duì)其方式球拦,這個(gè)和OC中的是一樣的靠闭,必須是8
的倍數(shù),不足的會(huì)自動(dòng)補(bǔ)齊坎炼,目的是以空間換時(shí)間
愧膀,來(lái)提高內(nèi)存的操作效率。
注意:這里的requiredSize
和requiredAligmentMask
都是上層函數(shù)傳進(jìn)來(lái)的
swift_allocObject 源碼分析
swift_allocObject
的源碼如下谣光,主要有一下幾部分組成
- 通過(guò)
swift_slowAlloc
分配內(nèi)存檩淋,并進(jìn)行內(nèi)存字節(jié)對(duì)齊。 - 通過(guò)
new + HeapObject + metadata
初始化一個(gè)實(shí)例對(duì)象 - 函數(shù)的返回值是
HeapObject
類型萄金,所以當(dāng)前對(duì)象的內(nèi)存結(jié)構(gòu)
就是HeapObject
的內(nèi)存結(jié)構(gòu)
- 進(jìn)入
swift_slowAlloc
函數(shù)蟀悦,其內(nèi)部主要是通過(guò)malloc
在堆
中分配size大小的內(nèi)存空間
媚朦,并返回內(nèi)存地址
,主要是用于存儲(chǔ)實(shí)例變量
- 進(jìn)入
HeapObject
初始化方法日戈,可以看到兩個(gè)參數(shù):metadata询张、refCounts
- 其中
metadata
類型是HeapMetadata
,是一個(gè)指針類型浙炼,占8
個(gè)字節(jié) -
refCounts
(引用計(jì)數(shù)份氧,類型是InlineRefCounts
,而InlineRefCounts
是一個(gè)RefCounts
的別名弯屈,占8
個(gè)字節(jié))
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
總結(jié)
- 對(duì)于實(shí)例對(duì)象
t
來(lái)說(shuō)蜗帜,其本質(zhì)是一個(gè)HeapObject
結(jié)構(gòu)體,默認(rèn)16
字節(jié)內(nèi)存大小(metadata
8字節(jié) +refCounts
8字節(jié))季俩,與OC對(duì)比如下:- OC中實(shí)例對(duì)象的本質(zhì)是
結(jié)構(gòu)體
钮糖,是以objc_object
為模板繼承的,其中有一個(gè)isa
指針酌住,占8
字節(jié)店归。 - Swift中實(shí)例對(duì)象,默認(rèn)的比OC中多一個(gè)
refCounts
引用計(jì)數(shù)酪我,默認(rèn)屬性占16
字節(jié)消痛。
- OC中實(shí)例對(duì)象的本質(zhì)是
- Swift中對(duì)象的分配流程是:
__allocating_init
--->swift_allocObject
--->_swift_allocObject_
--->swift_slowAlloc
-->malloc
。 - init在其中的職責(zé)就是初始化變量都哭,這點(diǎn)與OC中的是一致的秩伞。
看到這里,有的同學(xué)可能會(huì)有一些疑問(wèn)?:為什么 age
和 name
分別是 8字節(jié)
和 16字節(jié)
?
接下來(lái)我們?cè)敿?xì)講解一下:
對(duì)于Int
欺矫、String
類型纱新,在Swift中,兩個(gè)都是結(jié)構(gòu)體類型穆趴。我可以通過(guò)內(nèi)存打印來(lái)驗(yàn)證一下
//********* Int底層定義 *********
@frozen public struct Int : FixedWidthInteger, SignedInteger {...}
//********* String底層定義 *********
@frozen public struct String {...}
//********* 驗(yàn)證 *********
print("Int類型所占內(nèi)存大辛嘲:\(MemoryLayout<Int>.stride)")
print("String類型所占內(nèi)存大小:\(MemoryLayout<String>.stride)")
print("t.age所占內(nèi)存大形疵谩:\(MemoryLayout.size(ofValue: t.age))")
print("t.name所占內(nèi)存大胁痉稀:\(MemoryLayout.size(ofValue: t.name))")
//********* 打印結(jié)果 *********
Int類型所占內(nèi)存大小:8
String類型所占內(nèi)存大新缢:16
t.age所占內(nèi)存大凶迕省:8
t.name所占內(nèi)存大小:16
通過(guò)上面的打印結(jié)果化戳,可以清楚的看到Int
和 String
所占內(nèi)存大小单料。
但是到這里,可能同學(xué)問(wèn)了,那如果
String
的值非常大的話看尼,內(nèi)存里面分配的16個(gè)自己又該怎么存儲(chǔ)呢递鹉?
swift的內(nèi)存分配的問(wèn)題,之后會(huì)單獨(dú)寫一篇文章來(lái)詳細(xì)講一下藏斩,這里就不做詳細(xì)的贅述躏结,這里記住Int
占8個(gè)字節(jié),String
占16個(gè)字節(jié)就可以了狰域。其它的類型媳拴,可自行打印出來(lái)看一下。
下面我們來(lái)看另一個(gè)問(wèn)題兆览。我們上面提到了metadata
屈溉,那它到底是什么呢?我們繼續(xù)往下分析
Swift中 類結(jié)構(gòu)的探索
在OC中類是從objc_class
模板繼承過(guò)來(lái)的抬探。
在Swift中子巾,類的結(jié)構(gòu)在底層是HeapObject
,包含metadata
和 refCounts
小压。
而metadata
的類型是HeapMetadata
HeapMetadata類型分析
- 通過(guò)Swift源碼线梗,跟進(jìn)去我們會(huì)發(fā)現(xiàn)
using HeapMetadata = TargetHeapMetadata<InProcess>
- 我們接著跟進(jìn)
TargetHeapMetadata
template <typename Runtime>
struct TargetHeapMetadata : TargetMetadata<Runtime> {
using HeaderType = TargetHeapMetadataHeader<Runtime>;
TargetHeapMetadata() = default;
//初始化方法
constexpr TargetHeapMetadata(MetadataKind kind)
: TargetMetadata<Runtime>(kind) {}
#if SWIFT_OBJC_INTEROP
constexpr TargetHeapMetadata(TargetAnyClassMetadata<Runtime> *isa)
: TargetMetadata<Runtime>(isa) {}
#endif
};
進(jìn)入
TargetHeapMetadata
定義后,我們發(fā)現(xiàn)其本質(zhì)是一個(gè)模板類型
怠益。這個(gè)結(jié)構(gòu)體中沒(méi)有屬性仪搔,只有初始化
方法,傳入一個(gè)MetadataKind
類型的參數(shù)蜻牢,這里kind
就是傳入的Inprocess
接下來(lái)烤咧,我們繼續(xù)跟進(jìn)到
TargetMetadata
中,有一個(gè)kind
的屬性抢呆,kind
的類型就是前面?zhèn)魅氲?code>Inprocess煮嫌。然后我們?cè)俑M(jìn)Inprocess
,最總得到結(jié)果是:對(duì)于kind
抱虐,其類型的本質(zhì)是unsigned long
//******** TargetMetaData 定義 ********
struct TargetMetadata {
using StoredPointer = typename Runtime::StoredPointer
...
private:
/// The kind. Only valid for non-class metadata; getKind() must be used to get
/// the kind value.
StoredPointer Kind
}
//******** Inprocess 定義 ********
struct InProcess {
static constexpr size_t PointerSize = sizeof(uintptr_t);
using StoredPointer = uintptr_t;
...
}
//******** uintptr_t 定義 ********
typedef unsigned long uintptr_t;
- 從
TargetHeapMetadata
立膛、TargetMetadata
定義中,我可以看出kind
的類型是MetadataKind
(此處是強(qiáng)制轉(zhuǎn)化梯码,與上文并不沖突) - 跟進(jìn)
MetadataKind
,可以看到一個(gè)#include "MetadataKind.def"
好啰,再次跟進(jìn)轩娶,會(huì)看到所有類型的元數(shù)據(jù)
總結(jié)之后得到如下表格:
name | Value |
---|---|
Class | 0x0 |
Struct | 0x200 |
Enum | 0x201 |
Optional | 0x202 |
ForeignClass | 0x203 |
Opaque | 0x300 |
Tuple | 0x301 |
Function | 0x302 |
Existential | 0x303 |
Metatype | 0x304 |
ObjCClassWrapper | 0x305 |
ExistentialMetatype | 0x306 |
HeapLocalVariable | 0x400 |
HeapGenericLocalVariable | 0x500 |
ErrorObject | 0x501 |
LastEnumerated | 0x7FF |
- 回到
TargetMetadata
結(jié)構(gòu)體定義中,找到方法getClassObject
, 在該方法中去匹配kind
框往,返回值TargetClassMetadata
類型 - 如果是
Class
鳄抒,則直接對(duì)this
(當(dāng)前指針,即metadata) 強(qiáng)轉(zhuǎn)為ClassMetadata
/// Get the class object for this type if it has one, or return null if the
/// type is not a class (or not a class with a class object).
const TargetClassMetadata<Runtime> *getClassObject() const
//************* 方法的實(shí)現(xiàn) **************
template<> inline const ClassMetadata *
Metadata::getClassObject() const {
//匹配kind
switch (getKind()) {
//如果kind是class
case MetadataKind::Class: {
// Native Swift class metadata is also the class object.
// 將當(dāng)前指針強(qiáng)轉(zhuǎn)為ClassMetadata類型
return static_cast<const ClassMetadata *>(this);
}
case MetadataKind::ObjCClassWrapper: {
// Objective-C class objects are referenced by their Swift metadata wrapper.
auto wrapper = static_cast<const ObjCClassWrapperMetadata *>(this);
return wrapper->Class;
}
// Other kinds of types don't have class objects.
default:
return nullptr;
}
//************** ClassMetadata ****************
using ClassMetadata = TargetClassMetadata<InProcess>;
所以,TargetMetadata
和 TargetClassMetadata
本質(zhì)上是一樣的许溅,因?yàn)樵趦?nèi)存結(jié)構(gòu)中瓤鼻,可以直接進(jìn)行指針的轉(zhuǎn)換,所以我們可以認(rèn)為:結(jié)構(gòu)體
其實(shí)就是TargetClassMetadata
- 進(jìn)入
TargetClassMetadata
定義贤重,發(fā)現(xiàn)其繼承自TargetAnyClassMetadata
template <typename Runtime>
struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> {
...
/// Swift-specific class flags.
///Swift 特有的標(biāo)識(shí)
ClassFlags Flags;
/// The address point of instances of this type.
///此類型的實(shí)例對(duì)象的地址
uint32_t InstanceAddressPoint;
/// The required size of instances of this type.
/// 'InstanceAddressPoint' bytes go before the address point;
/// 'InstanceSize - InstanceAddressPoint' bytes go after it.
/// 實(shí)例對(duì)象內(nèi)存大小
uint32_t InstanceSize;
/// The alignment mask of the address point of instances of this type.
/// 實(shí)例對(duì)象內(nèi)存對(duì)齊
uint16_t InstanceAlignMask;
/// Reserved for runtime use.
/// 運(yùn)行時(shí)保留字段
uint16_t Reserved;
/// The total size of the class object, including prefix and suffix
/// extents.
/// 類的內(nèi)存大小
uint32_t ClassSize;
/// The offset of the address point within the class object.
/// 類的內(nèi)存首地址
uint32_t ClassAddressPoint;
...
}
- 進(jìn)入
TargetAnyClassMetadata
茬祷,又會(huì)發(fā)現(xiàn),其繼承自TargetHeapMetadata
template <typename Runtime>
struct TargetAnyClassMetadata : public TargetHeapMetadata<Runtime> {
using StoredPointer = typename Runtime::StoredPointer;
using StoredSize = typename Runtime::StoredSize
...
}
總結(jié)
綜上所述并蝗,當(dāng)metadata
的kind
為Class時(shí)祭犯,有如下繼承關(guān)系:
- 當(dāng)前類返回的實(shí)際類型是
TargetClassMetadata
;從上面繼承鏈可以看出TargetMetadata
只有一個(gè)屬性kind
滚停,TargetAnyClassMetaData
中有4個(gè)屬性:kind
沃粗、superClass
、cacheData
键畴、data
最盅。
- 當(dāng)前
Class
在內(nèi)存中存放的屬性,是由TargetClassMetadata
+TargetAnyClassMetadata
+TargetMetadata
的屬性加起來(lái)構(gòu)成的起惕,因此得出metadata
的數(shù)據(jù)結(jié)構(gòu)體如下:
struct swift_class_t:NSObject {
void *kind;//相當(dāng)于OC中的isa涡贱,kind的實(shí)際類型是unsigned long
void *superClass;
void *cacheData;
void *data;
uint32_t flages; //4字節(jié)
uint32_t instanceAddressOffset; //4字節(jié)
uint32_t instanceSize; //4字節(jié)
uint16_t instanceAlignMask; //2字節(jié)
uint16_t reserved; //2字節(jié)
uint32_t classSize; //4字節(jié)
uint32_t classAddressOffset; //4字節(jié)
void *description
...
}
與OC對(duì)比
- 實(shí)例對(duì)象 & 類
- OC中的
實(shí)例對(duì)象
本質(zhì)是結(jié)構(gòu)體
,是通過(guò)底層的objc_object
模板創(chuàng)建的疤祭,類是繼承自objc_class
- Swift中的
實(shí)例對(duì)象
本質(zhì)也是結(jié)構(gòu)體
盼产,類型是HeapObject
,比OC多了一個(gè)refCounts
- OC中的
- 方法列表
- OC中的方法儲(chǔ)存在
objc_class
結(jié)構(gòu)體class_rw_t
的methodList
中 - Swift中的方法儲(chǔ)存在
metadata
元數(shù)據(jù)中
- OC中的方法儲(chǔ)存在
- 引用計(jì)數(shù)
- OC中的ARC維護(hù)的是
散列表
- Swift中的ARC是對(duì)象內(nèi)部有一個(gè)
refCounts
屬性
- OC中的ARC維護(hù)的是