一.函數類型
函數本身也有自己的類型,它由形式參數類型和返回類型組成
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
//var a: (Int, Int) -> Int
var a = addTwoInts
func addTwoInts(_ a: Double, _ b: Double) -> Double {
return a + b
}
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
//如果不注明函數類型的話,因為有2個同名函數,此時編譯器是沒法知道是哪個函數。
var a: (Double, Double) -> Double = addTwoInts
a(10, 20)
var b = a
b(20 ,30)
通過LLDB分析函數a和函數b
(lldb) po withUnsafePointer(to: &a){print($0)}
0x00007ff7bfefecc0
0 elements
(lldb) x/8g 0x00007ff7bfefecc0
0x7ff7bfefecc0: 0x00007ff852b30120 0x000000010023eab0
0x7ff7bfefecd0: 0x00000001000f8060 0x00000001000c03a0
0x7ff7bfefece0: 0x0000000100003500 0x00000001000ac010
0x7ff7bfefecf0: 0x00007ff7bfeff2d0 0x0000000100003500
(lldb) cat address 0x00007ff852b30120
address:0x00007ff852b30120, 110c8type metadata for () <+8> , ($sytN), External: NO libswiftCore.dylib.__DATA_CONST.__const +110c8
(lldb) po withUnsafePointer(to: &b){print($0)}
0x00007ff7bfefecc0
0 elements
(lldb)
總結:函數在Swift中也是引用類型。
//將addTwoInts的metadata賦值給a
var a: (Double, Double) -> Double = addTwoInts
//將函數a的metadata賦值給b
var b = a
源碼中關于函數的Metadata。Metadata.h -> TargetFunctionTypeMetadata
//繼承自TargetMetadata(kind)
//當然里面也有kind
struct TargetFunctionTypeMetadata : public TargetMetadata<Runtime> {
using StoredSize = typename Runtime::StoredSize;
using Parameter = ConstTargetMetadataPointer<Runtime, swift::TargetMetadata>;
//存放的是函數類型的掩碼
TargetFunctionTypeFlags<StoredSize> Flags;
/// The type metadata for the result type.
ConstTargetMetadataPointer<Runtime, swift::TargetMetadata> ResultType;
Parameter *getParameters() { return reinterpret_cast<Parameter *>(this + 1); }
//關于參數是一個連續的內存空間
const Parameter *getParameters() const {
return reinterpret_cast<const Parameter *>(this + 1);
}
...
//參數個數
StoredSize getNumParameters() const {
return Flags.getNumParameters();
}
...
}
//Flags的掩碼枚舉
enum : int_type {
NumParametersMask = 0x0000FFFFU,
ConventionMask = 0x00FF0000U,
ConventionShift = 16U,
ThrowsMask = 0x01000000U,
ParamFlagsMask = 0x02000000U,
EscapingMask = 0x04000000U,
DifferentiableMask = 0x08000000U,
GlobalActorMask = 0x10000000U,
AsyncMask = 0x20000000U,
SendableMask = 0x40000000U,
// NOTE: The next bit will need to introduce a separate flags word.
};
基于源碼總結TargetFunctiontypeMetadata
數據結構,并且代碼驗證
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
struct TargetFunctionTypeMetadata {
var kind: Int
var flags: Int
var resultType: Any.Type
var paramaters: FunctionTypeParamater<Any.Type>
//參數個數
func parameterCount() -> Int {
return flags & 0x0000FFFF
}
}
struct FunctionTypeParamater<Element> {
var parameter: Element
mutating func value(index: Int) -> UnsafePointer<Element> {
return withUnsafePointer(to: &self) {
UnsafeRawPointer($0.advanced(by: index)).assumingMemoryBound(to: Element.self)
}
}
mutating func values(count: Int) -> UnsafeBufferPointer<Element> {
return withUnsafePointer(to: &self) {
UnsafeBufferPointer(start: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self), count: count)
}
}
}
/*
注意這里需要將函數的Type轉化為Metadata,而不是函數。因此需要使用到type(of:)來獲取函數的類型
as Any.Type,目前知道的只有在Class時可以不轉化為Any.Type.
結構體、枚舉、函數都需要使用as Any.Type來轉化一下,否則編譯器會認為是不同大小轉化報錯
至于原因,暫時沒有搞清楚。例如結構體沒有轉化前是Struct.Type,轉化后是Any.Type.
*/
let funcTypeMetadata = unsafeBitCast(type(of: addTwoInts) as Any.Type, to: UnsafeMutablePointer<TargetFunctionTypeMetadata>.self)
print("參數個數為", funcTypeMetadata.pointee.parameterCount())
let parametertypes = funcTypeMetadata.pointee.paramaters.values(count: funcTypeMetadata.pointee.parameterCount())
for (index, type) in parametertypes.enumerated() {
print("第\(index)個參數類型為:\(type)")
}
print("返回值類型:\(funcTypeMetadata.pointee.resultType)")
二.什么是閉包
閉包是一個捕獲了上下文的常量或變量的函數
1.官方案例認識閉包
/*
此時的外部函數makeIncrementer執行return后就釋放掉了
此時的內部函數incrementer會比外部函數的生命周期長,取決于API調用者的時機
此時的runningTotal外部函數變量,外部函數已經釋放了,但是內部函數想要使用到外部函數的變量,
此時就將外部函數的變量捕獲進來,才能保證內部函數在使用的過程中正常使用這個變量
此時runningTotal和incrementer()就形成了一個閉包。
也就印證了,閉包的定義。捕獲了上下文的常量/變量的函數
*/
func makeIncrementer() -> () -> Int {
var runningTotal = 10
func incrementer() -> Int {
runningTotal += 1
return runningTotal
}
return incrementer
}
2.閉包表達式
{ (param) -> (returnType) in
//do something
}
- 作用域(也就是大括號)
- 參數和返回值
- 函數體(in)之后的代碼
Swift中的閉包既可以當做變量,也可以當做參數傳遞
let closure: (Int) -> Int = {(age: Int) in
return age
}
也可以作為函數的參數
func test(param: () -> Int) {
print(param())
}
test {
return 10
}
3.尾隨閉包
當我們把閉包表達式作為函數的最后一個參數時,如果閉包表達式很長,我們可以通過尾隨閉包的書寫方式來提高代碼的可讀性。
func test(_ a: Int, _ b: Int, _ c: Int, by: (_ item1: Int, _ item2: Int, _ item3: Int) -> Bool) -> Bool{
return by(a, b, c)
}
test(10, 20, 30, by: {(_ item1: Int, _ item2: Int, _ item3: Int) -> Bool in
return (item1 + item2 < item3)
})
test(10, 20, 30){
return ($0 + $1 < $2)
}
閉包表達式是Swift語法。使用閉包表達式能更簡潔的傳達消息。當然閉包表達式的好處有很多:
- 利用上下文推斷參數和返回值類型
- 單表達式可以隱式返回,既省略
return
關鍵字 - 參數名稱的簡寫(比如我們的 $0)
- 尾隨閉包表達式
var array = [1, 2, 3]
array.sort(by: {(item1 : Int, item2: Int) -> Bool in return item1 < item2 })
array.sort(by: {(item1, item2) -> Bool in return item1 < item2 })
array.sort(by: {(item1, item2) in return item1 < item2 })
array.sort{(item1, item2) in item1 < item2 }
array.sort{ return $0 < $1 }
array.sort{ $0 < $1 }
array.sort(by: <)
三.捕獲值
1.Block中捕獲值
-(void)testBlock {
NSInteger i = 1;
/*
關于block捕獲變量原理,是將i的值(1)捕獲到了block中
block中訪問i的值,其實就是捕獲進來的i值。也就是捕獲進來的變量i(1)
i += 1;只會修改外部定義的i變量,并不能影響捕獲進來的i的值
如需更細則的探究,可去研究一下block底層原理
*/
void (^block)(void) = ^{
NSLog(@"block: %ld", i);
};
i += 1;
NSLog(@"before block: %ld", i); // 2
block(); // 1
NSLog(@"after block: %ld", i); // 2
}
如果我們想要外部的修改能夠影響當前block內部捕獲的值,我們只需要對當前捕獲的變量添加__block
修飾符
-(void)testBlock1 {
/*
當添加上__block修飾符后,會將i捕獲到當前的堆區。
此時無論是block內部還是外部,其實都是同一份內存空間的數據。因此無論哪個地方修改了數據,都會發生改變
*/
__block NSInteger i = 1;
void (^block)(void) = ^{
NSLog(@"block: %ld", i);
i += 1;
};
i += 1;
NSLog(@"before block: %ld", i); // 2
block(); // 2
NSLog(@"after block: %ld", i); // 3
}
2.閉包中的捕獲值
將上面的Block案例轉化為Swift代碼
var i = 1
/*
執行到closure時,通過SIL可知,編譯器會在全局變量的地址里面放我們當前的i,簡單的來說就是closure中的i其實就是外部的全局變量i。
%0 = global_addr @$s4mainliSivp : $*Int
*/
let closure = {
print("closure:", i)
}
i += 1;
print("before closure:", i) // 2
closure() // 2
print("after closure:", i) // 2
可能有的人會說這段代碼沒有放在函數里,那么我們將上面的代碼放入函數里
func test() {
var i = 1
/*
通過SIl分析,會將變量i捕獲到堆區。邏輯與__block類似,closure內部和外部的變量i訪問的是同一塊內存空間
*/
let closure = {
print("closure:", i)
i += 1
}
i += 1;
print("before closure:", i) // 2
closure() // 2
print("after closure:", i) // 3
}
test()
3.閉包是否和Block一樣有不同的類型(全局、堆區、棧區)
回顧Block中的不同類型(全局、堆區、棧區)
- (void)testBlock2 {
NSObject *o = [NSObject new];
NSLog(@"%ld", CFGetRetainCount((__bridge CFTypeRef)o)); // 1
/*
全局block,block內部不使用外部變量,只使用靜態變量/全局變量
堆block:使用局部變量/OC屬性,賦值給強引用/copy修飾
棧block:使用局部變量/OC屬性,沒有賦值給強引用/copy修飾
*/
/*
strongBlock 堆block
為什么這里是3呢?
參考下方的copyBlock
1.被棧block捕獲,引用計數+1
2.由棧block變為堆block,引用計數+1
*/
void(^strongBlock)(void) = ^{
NSLog(@"%ld", CFGetRetainCount((__bridge CFTypeRef)o)); // 3
};
strongBlock();
void(^__weak weakBlock)(void) = ^{
NSLog(@"%ld", CFGetRetainCount((__bridge CFTypeRef)o)); // 4
};
weakBlock();
/*
棧block執行copy后,由棧block變為堆block。引用計數+1
*/
void(^copyBlock)(void) = [weakBlock copy];
copyBlock();
}
但是在閉包里就沒有類型的概念了
var i = 10
/*
無論閉包內部的變量是全局/局部變量,此時的closure存放的是metadata,closure也是特殊的函數
*/
let closure = {
print("closure", i)
}
closure()
4.理解閉包的引用類型
func makeIncreasemer() -> () -> Int {
var runningTotal = 10
func increasmer() -> Int {
runningTotal += 1
return runningTotal
}
return increasmer
}
//makeInc理解為引用類型,操作的是同一塊內存空間的runningTotal
let makeInc = makeIncreasemer()
print(makeInc()) // 11
print(makeInc()) // 12
print(makeInc()) // 13
//相當于創建了3個不同的實例對象
print(makeIncreasemer()()) // 11
print(makeIncreasemer()()) // 11
print(makeIncreasemer()()) // 11
通過SIL分析
//increasmer的SIL代碼
//這里很清楚的能夠看到,當捕獲外部變量時,會調用project_box
%1 = project_box %0 : ${ var Int }, 0 // users: %16, %4, %2
Given a box T reference, produces the address of the value inside the box.
從@box T reference取出一個value的值
@box T reference對應的就是alloc_box
Allocates a reference-counted @box on the heap large enough to hold a value of type T, along
with a retain count and any other metadata required by the runtime.
The result of the instruction is the reference-counted @box reference that owns the box.
The project_box instruction is used to retrieve the address of the value inside the box.
分配一個足夠大的堆空間來容納T以及引用計數和運行時所需要的任何metadata。
簡單來說就是分配一個堆空間(box),存放metdadata、refCount、T。
project_box也就意味著取這個box的地址。box可以理解為實例對象
總結:因此當編譯器執行到runningTotal += 1
時,發現此時需要捕獲runningTotal
,此時就會去堆區開辟內存空間來存放metadata、refCount、runningTotal
,因此runningTotal就會變成實例對象的一部分
5.總結閉包
- 閉包能從上下文捕獲常量/變量,即使原數據的作用域不在也可以修改數據的值。例如runningTotal
- 每次修改捕獲值的時候,其實是修改堆區內存空間的值
- 每次執行
makeIncreasemer()()
會分配不同的內存空間
四.閉包的本質
0.IR基本語法
我們發現從SIL中看不出來有用的信息,降級為IR
代碼來探究閉包
i64
代表Int64
i32
代表Int32
i8
代表Int8或void *,根據上下文語義具體分析
數組
[<elementnumber> x <elementtype>]
//example
alloca [24 x i8], align 8 24個i8都是0
alloca [4 x i32] === array
結構體
%swift.refcounted = type {%swift.type*, i64}
//表示形式
%T = type {<type list>}
//example
{ i32, i32, i32 } ; 包含3個i32類型的結構體
{ float, i32 (i32) * } ; 成員是float類型和函數指針類型的結構體
指針
<type> *
//example
i64* //64位的整型
getelementptr
LLVM中我們獲取數組和結構體成員,通過getelementptr
,語法規則如下:
<result> = getelementptr <ty>, <ty>* <ptrval>{, [inrange] <ty> <idx>}*
<result> = getelementptr inbounds <ty>, <ty>* <ptrval>{, [inrange] <ty> <idx>}*
官方案例理解getelementptr
struct munger_struct {
int f1;
int f2;
};
void munge(struct munger_struct *P) {
P[0].f1 = P[1].f1 + P[2].f2;
}
//表示取munger_struct的地址,第一個索引表示在munger_struct上進行的偏移(munger_struct大小 * index),返回的數據類型還是munger_struct。
//返回數據的類型取決于*前面對于的類型
getelementptr inbounds %struct.munger_struct, %struct.munger_struct * %1, i64 0
//表示取出munger_struct中的f1,第二個索引表示在第一個索引確定的數據后,根據索引取內部的數據。
getelementptr inbounds %struct.munger_struct, %struct.munger_struct * %1, i64 0, i64 0
//表示取出munger_struct中的f2
getelementptr inbounds %struct.munger_struct, %struct.munger_struct * %1, i64 0, i64 1
int main(int argc, const char * argv[]) {
int array[4] = {1, 2, 3, 4};
int a = array[0];
return 0;
}
其中 int a = array[0] 這句對應的LLVM代碼應該是這樣的:
a = getelementptr inbounds [4 x i32], [4 x i32] * array, i32 0, i32 0
總結:
- 第一個索引不會改變返回的指針的類型,也就是說ptrval前面的*對應什么類型,返回就是什么類型
- 第一個索引的偏移量是由第一個索引的值和第一個ty執行的基本類型共同確定的
- 后面的索引是在數組/結構體內進行索引
- 每增加一個索引,就會使得該索引使用的基本類型和返回的指針類型去掉一層
1.閉包的本質是什么(IR)
我們熟悉了IR相關概念后,我們通過IR來分析Swift閉包
Swift代碼
func makeIncreasemer() -> () -> Int {
var runningTotal = 10
func increasmer() -> Int {
runningTotal += 1
return runningTotal
}
return increasmer
}
let makeInc = makeIncreasemer()
makeInc()
分析IR代碼
%swift.function = type { i8*, %swift.refcounted* }
%swift.refcounted = type { %swift.type*, i64 } ---> {i64, i64}
%swift.type = type { i64 }
%swift.full_boxmetadata = type { void (%swift.refcounted*)*, i8**, %swift.type, i32, i8* }
%TSi = type <{ i64 }>
// main函數
define i32 @main(i32 %0, i8** %1) #0 {
entry:
%2 = bitcast i8** %1 to i8*
// $s4main15makeIncreasemerSiycyF ---> main.makeIncreasemer() -> () -> Swift.Int
// { i8*, %swift.refcounted* } ----> {i8*, i64, i64},由%swift.refcounted分析得來
%3 = call swiftcc { i8*, %swift.refcounted* } @"$s4main15makeIncreasemerSiycyF"()
// 從寄存器%3提取數據, %4 = 第0個元素(i8*), %5 = 第1個元素%swift.refcounted({i64, i64})
%4 = extractvalue { i8*, %swift.refcounted* } %3, 0
%5 = extractvalue { i8*, %swift.refcounted* } %3, 1
// $s4main7makeIncSiycvp ---> main.makeInc : () -> Swift.Int
// 將%4賦值給makeInc結構體的第0個元素
store i8* %4, i8** getelementptr inbounds (%swift.function, %swift.function* @"$s4main7makeIncSiycvp", i32 0, i32 0), align 8
// 將%5賦值給makeInc結構體的第1個元素
store %swift.refcounted* %5, %swift.refcounted** getelementptr inbounds (%swift.function, %swift.function* @"$s4main7makeIncSiycvp", i32 0, i32 1), align 8
// 上面2步執行完成,相當于makeInc = 返回的閉包結構體。執行了賦值的操作
// 將結構體中的i8*存入寄存器%6
// 將結構體中的%swift.refcounted*存入寄存器%7
%6 = load i8*, i8** getelementptr inbounds (%swift.function, %swift.function* @"$s4main7makeIncSiycvp", i32 0, i32 0), align 8
%7 = load %swift.refcounted*, %swift.refcounted** getelementptr inbounds (%swift.function, %swift.function* @"$s4main7makeIncSiycvp", i32 0, i32 1), align 8
// 對%swift.refcounted*執行引用計數+1的操作,也就是執行了let makeInc = makeIncreasemer()
%8 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %7) #1
// 將%6強轉為Int64
%9 = bitcast i8* %6 to i64 (%swift.refcounted*)*
// 執行寄存器%9函數,傳入參數%7(%swift.refcounted*)。也就是執行了makeInc()。返回值為Int64,
%10 = call swiftcc i64 %9(%swift.refcounted* swiftself %7)
// 釋放寄存器%7(%swift.refcounted*)的引用
call void @swift_release(%swift.refcounted* %7) #1
ret i32 0
}
- 閉包的數據結構為
{i8*, i64, i64}
- 使用變量接收一個閉包時,實際上會先執行
@swift_retain
對%swift_refcounted*
添加引用計數,側面的證明閉包就是一個引用類型 - 執行閉包的時候其實是執行的
返回值 i8*(%swift_refcounted*)
。也就可以說明i8*
就是閉包執行的函數,而%swift_refcounted*
大概率就是HeapObject
翻譯為Swift代碼為
//{i8*, %swift_refcounted*}
struct ClosureData {
var ptr: UnsafeRawPointer
var object: HeapObject
}
struct HeapObject {
var metadata: UnsafeRawPointer
var refCount: Int
}
進入函數s4main15makeIncreasemerSiycyF
探究%swift_refcounted*
define hidden swiftcc { i8*, %swift.refcounted* } @"$s4main15makeIncreasemerSiycyF"() #0 {
entry:
// 開辟%TSi*(Int64位)的內存空間
%runningTotal.debug = alloca %TSi*, align 8
// 將內存空間地址強轉為i8*
%0 = bitcast %TSi** %runningTotal.debug to i8*
// 執行llvm清零函數
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
// 開辟堆區內存空間,類型為%swift.refcounted*
%1 = call noalias %swift.refcounted* @swift_allocObject(%swift.type* getelementptr inbounds (%swift.full_boxmetadata, %swift.full_boxmetadata* @metadata, i32 0, i32 2), i64 24, i64 7) #1
// 將%1強轉為{ %swift.refcounted, [8 x i8] }結構體
%2 = bitcast %swift.refcounted* %1 to <{ %swift.refcounted, [8 x i8] }>*
// %3為[8 x i8]的數組
%3 = getelementptr inbounds <{ %swift.refcounted, [8 x i8] }>, <{ %swift.refcounted, [8 x i8] }>* %2, i32 0, i32 1
// 將數組強轉為Int64
%4 = bitcast [8 x i8]* %3 to %TSi*
//將Int64存入%runningTotal.debug
store %TSi* %4, %TSi** %runningTotal.debug, align 8
// %._value = %TSi*結構體的第一條數據Int64
%._value = getelementptr inbounds %TSi, %TSi* %4, i32 0, i32 0
// 將10存入%._value,其實也就是將10存入了開辟的堆內存空間%1中
store i64 10, i64* %._value, align 8
// 添加引用計數
%5 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #1
// 執行realse
call void @swift_release(%swift.refcounted* %1) #1
// $s4main15makeIncreasemerSiycyF10increasmerL_SiyFTA ---> partial apply forwarder for increasmer #1 () -> Swift.Int in main.makeIncreasemer() -> () -> Swift.Int
// 第一個元素s4main15makeIncreasemerSiycyF10increasmerL_SiyFTA就是閉包函數地址
// 第二個元素將開辟的堆空間放入進去,同時10也存放在該堆空間上
%6 = insertvalue { i8*, %swift.refcounted* } { i8* bitcast (i64 (%swift.refcounted*)* @"$s4main15makeIncreasemerSiycyF10increasmerL_SiyFTA" to i8*), %swift.refcounted* undef }, %swift.refcounted* %1, 1
ret { i8*, %swift.refcounted* } %6
}
- 開辟了一塊
堆區內存空間(%swift_refcounted*)
- 將堆空間其中的
[8 x i8]存入數據10
(也就是捕獲值) - 將閉包執行的函數地址強轉為
i8*
, 將i8*
和堆區(%swift_refcounted*)
組成的結構體{i8* ,%swift_refcounted*}
返回
2.還原閉包結構體
有了關于IR代碼的邏輯后,此時我們可以通過Swift代碼來還原閉包結構體
func makeIncreasemer() -> () -> Int {
var runningTotal = 10
func increasmer() -> Int {
runningTotal += 1
return runningTotal
}
return increasmer
}
let makeInc = makeIncreasemer()
//{i8*, %swift_refcounted*}
//閉包的數據結構:閉包的執行地址 + 捕獲變量堆空間的地址
//Block的本質也是結構體
struct ClosureData<T> {
//閉包的地址
var ptr: UnsafeRawPointer
var object: UnsafePointer<Box<T>>
}
struct Box<T> {
var object: HeapObject
var value: T
}
struct HeapObject {
var metadata: UnsafeRawPointer
var refCount: Int
}
/*
為了獲取函數的數據類型,來實現內存的綁定
*/
struct NoMeanStruct {
var f: () -> Int
}
let f = NoMeanStruct(f: makeIncreasemer())
let ptr = UnsafeMutablePointer<NoMeanStruct>.allocate(capacity: 1)
ptr.initialize(to: f)
defer {
ptr.deinitialize(count: 1)
}
//報警告,建議使用withMemoryRebound。當然使用unsafeBitCast按位轉化也是沒有問題的
//let closureDataPtr = unsafeBitCast(ptr, to: UnsafePointer<ClosureData<Box<Int>>>.self)
let closureData = ptr.withMemoryRebound(to: ClosureData<Box<Int>>.self, capacity: 1) {
$0.pointee
}
print(closureData.ptr) // 0x0000000100004dc0
print(closureData.object) // 0x0000000101d1c940
使用終端驗證閉包函數地址0x0000000100004dc0
? nm -p /Users/zt/Library/Developer/Xcode/DerivedData/swiftTest-hlhwnleuvbzgkogcaxdncrsezayx/Build/Products/Debug/swiftTest | grep 0000000100004dc0
0000000100004dc0 t _$s9swiftTest15makeIncreasemerSiycyF10increasmerL_SiyFTA
? xcrun swift-demangle s9swiftTest15makeIncreasemerSiycyF10increasmerL_SiyFTA
$s9swiftTest15makeIncreasemerSiycyF10increasmerL_SiyFTA ---> partial apply forwarder for increasmer #1 () -> Swift.Int in swiftTest.makeIncreasemer() -> () -> Swift.Int
nm -p mach-O地址 | grep 內存地址
獲取Mach-O中該內存地址數據
使用LLDB
驗證0x0000000101d1c940
(lldb) x/8g 0x0000000101d1c940
0x101d1c940: 0x000000010000c308 0x0000000200000003
0x101d1c950: 0x000000000000000a 0x0000000101d1cfa0
0x101d1c960: 0x0000000100004dc0 0x0000000101d1c940
0x101d1c970: 0x000000004d55545a 0x000020a000000000
驗證了IR
代碼邏輯,完成對閉包數據結構的還原
3.回顧之前的函數(addTwoInts)
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
var add = addTwoInts
IR代碼
define i32 @main(i32 %0, i8** %1) #0 {
entry:
%2 = bitcast i8** %1 to i8*
// $s4main3addyS2i_Sitcvp ---> main.add : (Swift.Int, Swift.Int) -> Swift.Int
// $s4main10addTwoIntsyS2i_SitF ---> main.addTwoInts(Swift.Int, Swift.Int) -> Swift.Int
// 將main.addTwoInts強轉成i8*,然后將這個i8*存入main.add的一個元素中
store i8* bitcast (i64 (i64, i64)* @"$s4main10addTwoIntsyS2i_SitF" to i8*), i8** getelementptr inbounds (%swift.function, %swift.function* @"$s4main3addyS2i_Sitcvp", i32 0, i32 0), align 8
// 在main.add中存入null到第二個元素中,也就是%swift.refcounted*
store %swift.refcounted* null, %swift.refcounted** getelementptr inbounds (%swift.function, %swift.function* @"$s4main3addyS2i_Sitcvp", i32 0, i32 1), align 8
// 函數本質為%swift.function*. type { i8*, %swift.refcounted* }
ret i32 0
}