Swift -- 7.閉包(上)

一.函數類型

函數本身也有自己的類型,它由形式參數類型返回類型組成

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

project_box

Given a box T reference, produces the address of the value inside the box.
從@box T reference取出一個value的值
@box T reference對應的就是alloc_box

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
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容