Swift編譯簡(jiǎn)介
SIL介紹
- SIL是
Swift Intermediate Language的簡(jiǎn)寫(xiě),SIL會(huì)對(duì)Swift進(jìn)行高級(jí)別的語(yǔ)意分析和優(yōu)化,我們通過(guò)解讀SIL代碼就能去了解Swift背后的一些實(shí)現(xiàn)細(xì)節(jié),幫助我們理解一些問(wèn)題。 -
IOS開(kāi)發(fā)語(yǔ)言不管OC還是Swift后端都是通過(guò)LLVM編譯的
image.png - OC通過(guò)Clang編譯器前端,編譯成IR,然后再生成目標(biāo)文件.o
- Swift是通過(guò)Swift編譯器前端,編譯成IR,再生成目標(biāo)文件.o
-
Swift編譯過(guò)程都經(jīng)歷了哪些步驟
image.png - Swift語(yǔ)言也是基于LLVM架構(gòu)的,你可以看到與現(xiàn)有Objective-C語(yǔ)言有很多相似之處,有相同的后端結(jié)構(gòu),前端的工作流程也基本類似,詞法分析,標(biāo)記,構(gòu)建AST,類型檢查,主要區(qū)別在于生成IR之前,AST之后,增加了SIL,它是AST和LLVM IR之間的另一種中間代碼表示形式。
這么做的目的是希望彌補(bǔ)一些Clang編譯器的缺陷,如無(wú)法執(zhí)行一些高級(jí)分析,可靠的診斷和優(yōu)化,而AST和LLVM IR都不是合適的選擇。因此,SIL應(yīng)運(yùn)而生,用來(lái)解決現(xiàn)有的缺陷。 -
通過(guò)命令查看swiftc能做什么樣的事情
image.png
SIL分析
- 在main.swift中輸入如下代碼
class XQTeacher {
var age:Int = 18
var name:String = "xq"
}
var t = XQTeacher()
-
通過(guò)命令
swiftc -dump-ast main.swift查看語(yǔ)法樹(shù)
image.png 通過(guò)命令
swiftc -emit-sil main.swift >> ./main.sil生成main.sil并用VSCode打開(kāi)它,我們來(lái)研究它的main函數(shù)
// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
alloc_global @$s4main1tAA9XQTeacherCvp // id: %2
%3 = global_addr @$s4main1tAA9XQTeacherCvp : $*XQTeacher // user: %7
%4 = metatype $@thick XQTeacher.Type // user: %6
// function_ref XQTeacher.__allocating_init()
%5 = function_ref @$s4main9XQTeacherCACycfC : $@convention(method) (@thick XQTeacher.Type) -> @owned XQTeacher // user: %6
%6 = apply %5(%4) : $@convention(method) (@thick XQTeacher.Type) -> @owned XQTeacher // user: %7
store %6 to %3 : $*XQTeacher // id: %7
%8 = integer_literal $Builtin.Int32, 0 // user: %9
%9 = struct $Int32 (%8 : $Builtin.Int32) // user: %10
return %9 : $Int32 // id: %10
} // end sil function 'main'
- @main這里標(biāo)識(shí)我們當(dāng)前main.swift 的入口函數(shù),SIL中的標(biāo)識(shí)符名稱以@作為前綴
- %0,%1...在SIL也叫做寄存器,這里我們可以理解為我們?nèi)粘i_(kāi)發(fā)中的常量,一旦賦值之后就不可以再修改,如果SIL中還要繼續(xù)使用,那么就不斷的累加數(shù)字。同時(shí)這里所說(shuō)的寄存器是虛擬的,最終運(yùn)行到我們的機(jī)器上,會(huì)使用真的寄存器。
- alloc_gobal創(chuàng)建一個(gè)全局變量
- global_ addr拿到全局變量的地址,賦值給%3
- metatype拿到XQTeacher的Metadata賦值給%4
- 將 allocating_init的函數(shù)地址賦值給%5
- apply調(diào)用 allocating_init,并把返回值給%6
- 將%6的值存儲(chǔ)到%3(也就是我們剛剛創(chuàng)建的全局變量的地址)
- 構(gòu)建Int,并return
- XQTeacher.__allocating_init()的實(shí)現(xiàn)
// XQTeacher.__allocating_init()
sil hidden [exact_self_class] @$s4main9XQTeacherCACycfC : $@convention(method) (@thick XQTeacher.Type) -> @owned XQTeacher {
// %0 "$metatype"
bb0(%0 : $@thick XQTeacher.Type):
%1 = alloc_ref $XQTeacher // user: %3
// function_ref XQTeacher.init()
%2 = function_ref @$s4main9XQTeacherCACycfc : $@convention(method) (@owned XQTeacher) -> @owned XQTeacher // user: %3
%3 = apply %2(%1) : $@convention(method) (@owned XQTeacher) -> @owned XQTeacher // user: %4
return %3 : $XQTeacher // id: %4
} // end sil function '$s4main9XQTeacherCACycfC'
通過(guò)符號(hào)斷點(diǎn)調(diào)試
- 在代碼新增符號(hào)斷點(diǎn)
_allocing_init,運(yùn)行代碼
image.png - 往下執(zhí)行到
swift_allocObject
image.png
源碼調(diào)試
- 在編譯好的源碼的
_swift_allocObject_方法加入一個(gè)斷點(diǎn)
image.png - 在
REPL(命令交互行)中編寫(xiě)代碼(也可以拷貝)
image.png - 查看左上角的調(diào)試數(shù)據(jù)其中requiredSize是分配的實(shí)際內(nèi)存大小,為40
- requiredAlignmentMask是swift中的字節(jié)對(duì)齊長(zhǎng)度,為8
swift_allocObject
-
_swift_allocObject_的源碼實(shí)現(xiàn)
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
size_t requiredSize,
size_t requiredAlignmentMask) {
assert(isAlignmentMask(requiredAlignmentMask));
auto object = reinterpret_cast<HeapObject *>(
swift_slowAlloc(requiredSize, requiredAlignmentMask));//分配內(nèi)存
// NOTE: this relies on the C++17 guaranteed semantics of no null-pointer
// check on the placement new allocator which we have observed on Windows,
// Linux, and macOS.
new (object) HeapObject(metadata);//初始化實(shí)例對(duì)象
// If leak tracking is enabled, start tracking this object.
SWIFT_LEAKS_START_TRACKING_OBJECT(object);
SWIFT_RT_TRACK_INVOCATION(object, swift_allocObject);
return object;
}
-
swift_slowAlloc分配內(nèi)存
void *swift::swift_slowAlloc(size_t size, size_t alignMask) {
void *p;
// This check also forces "default" alignment to use AlignedAlloc.
if (alignMask <= MALLOC_ALIGN_MASK) {
#if defined(__APPLE__)
p = malloc_zone_malloc(DEFAULT_ZONE(), size);
#else
p = malloc(size);
#endif
} else {
size_t alignment = (alignMask == ~(size_t(0)))
? _swift_MinAllocationAlignment
: alignMask + 1;
p = AlignedAlloc(size, alignment);
}
if (!p) swift::crash("Could not allocate memory.");
return p;
}
-
new (object) HeapObject(metadata);初始化實(shí)例對(duì)象
struct HeapObject {
/// This is always a valid pointer to a metadata object.
HeapMetadata const *metadata;
// InlineRefCounts refCounts
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
#ifndef __swift__
HeapObject() = default;
// Initialize a HeapObject header as appropriate for a newly-allocated object.
constexpr HeapObject(HeapMetadata const *newMetadata)
: metadata(newMetadata)
, refCounts(InlineRefCounts::Initialized)
{ }
......
}
- 實(shí)例對(duì)象是以
HeapObject來(lái)創(chuàng)建的,HeapObject里面有一個(gè)HeapMetadata類型的metadata,還有一個(gè)引用計(jì)數(shù)refCounts
using HeapMetadata = TargetHeapMetadata<InProcess>;
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
};
-
其內(nèi)部結(jié)構(gòu)如圖
image.png - 得到最終
Swift的metadata結(jié)構(gòu)體的構(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 flags; //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;
...
}
Swift屬性
存儲(chǔ)屬性
- 存儲(chǔ)屬性要么用
let修飾的常量,要么是var修飾的變量
class XQTeacher {
var age:Int = 18
var name:String = "xq"
}
- 上面的兩個(gè)屬性就是變量存儲(chǔ)屬性,我們也可以通過(guò)SIL查看
class XQTeacher {
@_hasStorage @_hasInitialValue var age: Int { get set }
@_hasStorage @_hasInitialValue var name: String { get set }
@objc deinit
init()
}
計(jì)算屬性
- 計(jì)算屬性:是指不占用內(nèi)存空間,本質(zhì)是set/get方法的屬性
- 通過(guò)打印下面這個(gè)類的實(shí)例對(duì)象內(nèi)存大小,打印結(jié)果是24,HeapObject默認(rèn)有16個(gè)字節(jié)的內(nèi)存,再加上width占8個(gè)字節(jié)
class Square {
var width:Double = 10
var area:Double {
get {
Double(width * width)
}
set {
width = sqrt(newValue)
}
}
}
print(class_getInstanceSize(Square.self))
- 查看sil文件,看到存儲(chǔ)屬性只有width,而area的本質(zhì)就是
set和get方法
class Square {
@_hasStorage @_hasInitialValue var width: Double { get set }
var area: Double { get set }
@objc deinit
init()
}
屬性觀察者
- 屬性觀察者就是
willSet和didSet
class XQTeacher {
var name:String = "xq" {
willSet {
print("newValue:\(newValue)")
}
didSet {
print("oldValue:\(oldValue)")
}
}
}
var t = XQTeacher()
t.name = "dp"
- 代碼執(zhí)行結(jié)果,可以知道在新值存儲(chǔ)之前調(diào)用了
willSet,新值存儲(chǔ)之后調(diào)用了didSet
newValue:dp
oldValue:xq
-
觀察sil,驗(yàn)證上文的結(jié)論
image.png - 再來(lái)看下一個(gè)情況,在
init方法里面調(diào)用set方法
class XQTeacher {
var name:String = "xq" {
willSet {
print("newValue:\(newValue)")
}
didSet {
print("oldValue:\(oldValue)")
}
}
init() {
self.name = "test"
}
}
var t = XQTeacher()
發(fā)現(xiàn)它并不會(huì)觸發(fā)屬性觀察者,原因是我們?cè)诔跏蓟臅r(shí)候設(shè)置屬性值的時(shí)候,會(huì)先調(diào)用
memset清理內(nèi)存空間,因?yàn)檫@塊內(nèi)存可能有殘留的臟數(shù)據(jù),此時(shí)我們?cè)?code>didSet里面調(diào)用其他屬性時(shí)有可能其他屬性內(nèi)存未被分配完成會(huì)訪問(wèn)一片未知的內(nèi)存空間,Swift是一門(mén)安全性的語(yǔ)言這種操作是不允許的那些地方可以添加屬性觀察者
類中定義的存儲(chǔ)屬性
class XQTeacher {
var name:String = "xq" {
willSet {
print("newValue:\(newValue)")
}
didSet {
print("oldValue:\(oldValue)")
}
}
}
- 繼承的存儲(chǔ)屬性
class XQSpecialTeacher: XQTeacher {
override var name: String {
willSet {
print("newValue:\(newValue)")
}
didSet {
print("oldValue:\(oldValue)")
}
}
}
- 繼承的計(jì)算屬性
class XQTeacher {
var age:Int = 18
var age2:Int {
get {
return age
}
set {
age = newValue
}
}
}
class XQSpecialTeacher: XQTeacher {
override var age2: Int {
willSet {
print("override newValue:\(newValue)")
}
didSet {
print("override oldValue:\(oldValue)")
}
}
}
- 父類和子類都實(shí)現(xiàn)了屬性觀察者,它的調(diào)用順序
class XQTeacher {
var age:Int = 18 {
willSet {
print("newValue:\(newValue)")
}
didSet {
print("oldValue:\(oldValue)")
}
}
var age2:Int {
get {
return age
}
set {
age = newValue
}
}
}
class XQSpecialTeacher: XQTeacher {
override var age: Int {
willSet {
print("override newValue:\(newValue)")
}
didSet {
print("override oldValue:\(oldValue)")
}
}
}
- 調(diào)用順序如下,子類和父類都實(shí)現(xiàn)了屬性觀察者,會(huì)先調(diào)用子類的
willSet,再調(diào)用父類的willSet,再調(diào)用父類的didSet,最后調(diào)用子類的didSet。
override newValue:99
newValue:99
oldValue:18
override oldValue:18
- 我們?cè)谧宇惖?code>init方法里面調(diào)用
set方法會(huì)不會(huì)觸發(fā)屬性觀察者呢
class XQSpecialTeacher: XQTeacher {
override var age: Int {
willSet {
print("override newValue:\(newValue)")
}
didSet {
print("override oldValue:\(oldValue)")
}
}
override init() {
super.init()
self.age = 66
}
}
- 通過(guò)打印結(jié)果可以看到,是會(huì)調(diào)用的,原因是調(diào)用了父類的
init已經(jīng)有默認(rèn)值了即屬性已經(jīng)初始化完成了,是可以調(diào)用屬性觀察者的。
延遲存儲(chǔ)屬性
- 1、使用lazy修飾的存儲(chǔ)屬性
class XQTeacher {
lazy var age:Int = 18
}
-
2、延遲屬性必須有一個(gè)默認(rèn)的初始值,沒(méi)有初始值會(huì)報(bào)錯(cuò)
image.png -
3、延遲存儲(chǔ)在第一次訪問(wèn)的時(shí)候才被賦值
image.png 4、延遲存儲(chǔ)屬性并不能保證線程安全
-
5、延遲存儲(chǔ)屬性對(duì)實(shí)例對(duì)象大小的影響,我們不使用延遲存儲(chǔ)屬性的時(shí)候內(nèi)存大小為24,使用之后為32
image.png 通過(guò)sil分析延遲存儲(chǔ)屬性
類型屬性
- 類型屬性屬于這個(gè)類本身,不管有多少實(shí)例,類型屬性只有一份,我們使用
static來(lái)聲明一個(gè)類型屬性
class XQTeacher {
static var age : Int = 18
}
var t = XQTeacher()
XQTeacher.age = 20
- 通過(guò)查看sil文件,可以看到多出了一個(gè)全局變量,類型屬性其實(shí)是一個(gè)全局變量
// static XQTeacher.age
sil_global hidden @$s4main9XQTeacherC3ageSivpZ : $Int
- 查看到main函數(shù)獲取age的方法,調(diào)用的是
XQTeacher.age.unsafeMutableAddressor
image.png -
XQTeacher.age.unsafeMutableAddressor里面調(diào)用了globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_func0
// XQTeacher.age.unsafeMutableAddressor
sil hidden [global_init] @$s4main9XQTeacherC3ageSivau : $@convention(thin) () -> Builtin.RawPointer {
bb0:
%0 = global_addr @globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_token0 : $*Builtin.Word // user: %1
%1 = address_to_pointer %0 : $*Builtin.Word to $Builtin.RawPointer // user: %3
// function_ref globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_func0
%2 = function_ref @globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_func0 : $@convention(c) () -> () // user: %3
%3 = builtin "once"(%1 : $Builtin.RawPointer, %2 : $@convention(c) () -> ()) : $()
%4 = global_addr @$s4main9XQTeacherC3ageSivpZ : $*Int // user: %5
%5 = address_to_pointer %4 : $*Int to $Builtin.RawPointer // user: %6
return %5 : $Builtin.RawPointer // id: %6
} // end sil function '$s4main9XQTeacherC3ageSivau'
-
globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_func0的實(shí)現(xiàn)
// globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_func0
sil private @globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_func0 : $@convention(c) () -> () {
bb0:
alloc_global @$s4main9XQTeacherC3ageSivpZ // id: %0
%1 = global_addr @$s4main9XQTeacherC3ageSivpZ : $*Int // user: %4 獲取到age的地址
%2 = integer_literal $Builtin.Int64, 18 // user: %3 初始化的值18
%3 = struct $Int (%2 : $Builtin.Int64) // user: %4
store %3 to %1 : $*Int // id: %4 將%3的值放到地址中
%5 = tuple () // user: %6
return %5 : $() // id: %6
} // end sil function 'globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_func0'
-
通過(guò)匯編調(diào)試查看
image.png -
繼續(xù)執(zhí)行
image.png - 可以看到會(huì)調(diào)用
swift_once,表示全局變量只會(huì)初始化一次,對(duì)應(yīng)sil中的builtin "once" - 通過(guò)源碼查看
swift_once的實(shí)現(xiàn),它的底層是調(diào)用一個(gè)gcd的單次執(zhí)行函數(shù)
/// Runs the given function with the given context argument exactly once.
/// The predicate argument must point to a global or static variable of static
/// extent of type swift_once_t.
void swift::swift_once(swift_once_t *predicate, void (*fn)(void *),
void *context) {
#if defined(__APPLE__)
dispatch_once_f(predicate, context, fn);
#elif defined(__CYGWIN__)
_swift_once_f(predicate, context, fn);
#else
std::call_once(*predicate, [fn, context]() { fn(context); });
#endif
}
實(shí)現(xiàn)單列
- 通過(guò)上文對(duì)
static的了解,我們可以用static來(lái)實(shí)現(xiàn)一個(gè)單列
class XQTeacher {
static let sharedInstance = XQTeacher()
private init() {
}
}
var t = XQTeacher.sharedInstance















