swift进阶(四)闭包底层探索

Swift进阶文章汇总

在文章swift进阶(四)闭包的使用介绍了闭包的使用,那么闭包底层是什么样的呢?那今天让我们探究一番。

1. 闭包捕获上下文

先看以下代码打印的值应该是多少?

func makeIncrementer() -> () -> Int {
    var runningTotal = 10
    func incrementer() -> Int {
        runningTotal += 1
        return runningTotal
    }
    return incrementer
}

let makeInc = makeIncrementer()
print(makeInc())
print(makeInc())
print(makeInc())
print("==========================")
print(makeIncrementer()())
print(makeIncrementer()())
print(makeIncrementer()())

打印结果如下:
在这里插入图片描述

查看SIL源码

  • 执行swiftc -emit-sil main.swift >> ./main.sil && code main.sil,生成sil文件

    • 在sil文件中查看makeIncrementer()方法,会发下alloc_box的地址赋值给了runningTotal变量。其中alloc_box是在堆上开辟空间。

    • 获取变量时,通过project_box在堆上取出来

    • 在闭包引用前,执行了retain操作

    • 在闭包引用后,执行了release操作

在这里插入图片描述

小结

  • 闭包捕获值的本质是在堆上开辟一块空间,将当前变量的值放到堆空间上。

2. LR简单语法介绍

数组

  • [数组的数量 x 数组存放的类型]
[<elementnumber> x <elementtype>]
 //example
alloca[24xi8],align8 24个i8类型,
  • in:多少位的整型,i8相当于8位,也就是1字节

结构体

  • %结构体名称 = type {<结构体成员类型,成员大小>}
%swift.refcounted = type { %swift.type*, i64 }

指针类型

<type> *
 //example
i64* //64的整型

getelementptr指令

获取数组、结构体中的元素
语法规则如下:

  • 数组:返回值 = getelementptr 当前索引的基本类型, 当前索引的地址,当前数组的index
  • 结构体:返回值 = getelementptr 当前索引的基本类型, 当前索引的地址,结构体指针的偏移量,当前数组的成员变量
<result> = getelementptr <ty>, <ty>* <ptrval>{, [inrange] <ty> <id x>}*
<result> = getelementptr inbounds <ty>, <ty>* <ptrval>{, [inrange] <ty> <idx>}*

例如:

struct munger_struct {
int f1;
int f2;
};
 void munge(struct munger_struct *P) { 
  P[0].f1 = P[1].f1 + P[2].f2;
  }

生成的LR语法:
在这里插入图片描述

3. 闭包的底层

  • 变量makeInc存放的是什么东西呢?

在这里插入图片描述

  • 查看SIL源码,发现只有一个赋值,无法看到其中赋值的是什么。
    在这里插入图片描述

  • 那就生成LR源码查看

    • makeInc存放的就是一个结构体指针

      • 其中结构体的成员变量有是i8 -> void *类型的指针
      • %swift.refcounted* 是 i64类型,其大小是i64的结构体

在这里插入图片描述
翻译成swift代码:

struct FuntionData<T>{
    var ptr: UnsafeRawPointer //
    var captureValue: UnsafePointer<T>
}
  • 查看结构体{ i8*, %swift.refcounted* } 的赋值

    • 内嵌函数地址存放到了i8*
    • 将%1的值放到了index=1的位置

在这里插入图片描述

  • 查看%swift.refcounted*的结构是 i64类型,其大小是i64的结构体 ,这个与HeapObject的结构有些相似

在这里插入图片描述
翻译成swift代码:

struct HeapObject{
    var type: UnsafeRawPointer
    var refCount1: UInt32
}
  • 其中1%创建出来之后,有一层转换,

    • 第一个成员变量是%swift.refcounted 的地址
    • 第二个成员变量[8 x i8]用来存储值

在这里插入图片描述

翻译成swift代码:

struct Box<T> {
    var refCounted: HeapObject
    var value: T
}
  • 根据上述的分析,将闭包的指针绑定到下列变量中,进行查看打印
struct HeapObject{
    var type: UnsafeRawPointer
    var refCount1: UInt32
}
//
struct FuntionData<T>{
    var ptr: UnsafeRawPointer //
    var captureValue: UnsafePointer<T>
}

struct Box<T> {
    var refCounted: HeapObject
    var value: T
}

//需要将当前闭包使用此结构体包装一层
struct VoidIntFun {
    var f: () ->Int
}

func makeIncrementer() -> () -> Int {
    var runningTotal = 12
    func incrementer() -> Int {
        runningTotal += 1
        return runningTotal
    }
    return incrementer
}

var makeInc = makeIncrementer(forIncrement())

var f = VoidIntFun(f: makeInc)

let ptr = UnsafeMutablePointer<VoidIntFun>.allocate(capacity: 1)

ptr.initialize(to: f)

let ctx = ptr.withMemoryRebound(to: FuntionData<Box<Int>>.self, capacity: 1){$0.pointee}

print(ctx.ptr)
print(ctx.captureValue)
  • 查看打印结果
    在这里插入图片描述
  • 在终端查看0x00000001000058c0,运行 nm -p 当前项目的可执行文件 | grep 00000001000058c0
    在这里插入图片描述

思考:如果捕获的是多个变量呢,它的内存结构是什么样子的呢?

例如下面:

func makeIncrementer(forIncrement amount: Int,_ amount1: Int) -> () -> Int {
    var runningTotal = 12
    var thrid = 12
    func incrementer() -> Int {
        runningTotal += amount + thrid
        return runningTotal
    }
    return incrementer
}

var makeInc = makeIncrementer(forIncrement: 10, 15)

使用上面分析的闭包原理打印下发现:

在这里插入图片描述

在这里插入图片描述

  • 那么说明结构体Box就需要修改一下成员变量
struct Box<T> {
    var refCounted: HeapObject
    var valueBox: UnsafeRawPointer
    var value: T
    var value2: T
}
  • 打印ctx.captureValue.pointee

在这里插入图片描述

  • 其完整代码如下:
struct HeapObject{
    var type: UnsafeRawPointer
    var refCount1: UInt32
    var refCount2: UInt32
}
//
struct FuntionData<T>{
    var ptr: UnsafeRawPointer //
    var captureValue: UnsafePointer<T>
}

struct Box<T> {
    var refCounted: HeapObject
    var valueBox: UnsafeRawPointer
    var value: T
    var value2: T
}

struct VoidIntFun {
    var f: () ->Int
}

func makeIncrementer(forIncrement amount: Int,_ amount1: Int) -> () -> Int {
    var runningTotal = 12
    var thrid = 12
    func incrementer() -> Int {
        runningTotal += amount + thrid
        return runningTotal
    }
    return incrementer
}

var makeInc = makeIncrementer(forIncrement: 10, 15)

var f = VoidIntFun(f: makeInc)

let ptr = UnsafeMutablePointer<VoidIntFun>.allocate(capacity: 1)

ptr.initialize(to: f)

let ctx = ptr.withMemoryRebound(to: FuntionData<Box<Int>>.self, capacity: 1){$0.pointee}

print(ctx.ptr)
print(ctx.captureValue.pointee)

小结

  • 捕获值的原理是:在堆区开辟了一块内存空间,将捕获的值放到堆上
  • 闭包是一个引用类型。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值