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
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容