class Clourse {
func outFunc() -> () ->Int{
var total:Int=10
func innerFunc()->Int{
total+=1
returntotal
}
return innerFunc
}
//通过命令:swiftc -emit-ir Clourse.swift > ./main.ll 将代码转换为IR文件
/*
define hidden swiftcc { i8*, %swift.refcounted* } @"$s7ClourseAAC7outFuncSiycyF"(%T7ClourseAAC* swiftself %0) #0 {
entry:
%self.debug = alloca %T7ClourseAAC*, align 8
%1 = bitcast %T7ClourseAAC** %self.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
%total.debug = alloca %TSi*, align 8
%2 = bitcast %TSi** %total.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
store %T7ClourseAAC* %0, %T7ClourseAAC** %self.debug, align 8
%3 = 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) #2
%4 = bitcast %swift.refcounted* %3 to <{ %swift.refcounted, [8 x i8] }>*
%5 = getelementptr inbounds <{ %swift.refcounted, [8 x i8] }>, <{ %swift.refcounted, [8 x i8] }>* %4, i32 0, i32 1
%6 = bitcast [8 x i8]* %5 to %TSi*
store %TSi* %6, %TSi** %total.debug, align 8
%._value = getelementptr inbounds %TSi, %TSi* %6, i32 0, i32 0
store i64 10, i64* %._value, align 8
%7 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %3) #2
call void @swift_release(%swift.refcounted* %3) #2
%8 = insertvalue { i8*, %swift.refcounted* } { i8* bitcast (i64 (%swift.refcounted*)* @"$s7ClourseAAC7outFuncSiycyF05innerC0L_SiyFTA" to i8*), %swift.refcounted* undef }, %swift.refcounted* %3, 1
ret { i8*, %swift.refcounted* } %8
}
*/
/*
分析IR代码
IR语法:
[8 x i8] 第一个8代表数组中存放数据的数量 i*8数组中存放数据的类型
- T: 结构体名称
- : 列表,即结构体的成员列表
getelementptr 指令: 在LLVM中获取数组和结构体的成员时通过getelementptr,
查看outFunc方法
1、首先通过swift_allocObject创建swift.refcounted结构体
2、然后将swift.refcounted 转换为<{ %swift.refcounted, [8 x i8] }>结构体 即:box
3、取出结构体中index等于1的成员变量,存储到[8 x i8] 连续的内存空间中
4、将内嵌函数的地址存储到i8即void地址中
5、最后返回一个结构体
*/
/*
总结:
1、捕获值原理:在堆上开辟内存空间,并将捕获的值放到这个内存空间里
2、修改捕获值是:实质是修改堆空间的值
3、闭包是一个引用类型(引用类型是地址传递),闭包的底层结构:内连函数地址+捕获变量的地址 == 闭包
4、函数也是一个引用类型(本质值一个结构体,其中只保存了函数的地址)
*/
/*
//打断点查看outFunc
SwiftStartValue`outFunc():
0x1006e8a2c <+0>: sub sp, sp, #0x20 ; =0x20
0x1006e8a30 <+4>: stp x29, x30, [sp, #0x10]
0x1006e8a34 <+8>: add x29, sp, #0x10 ; =0x10
0x1006e8a38 <+12>: adrp x8, 8
0x1006e8a3c <+16>: add x8, x8, #0x50 ; =0x50
0x1006e8a40 <+20>: add x0, x8, #0x10 ; =0x10
0x1006e8a44 <+24>: str xzr, [sp, #0x8]
0x1006e8a48 <+28>: mov w9, #0x18
0x1006e8a4c <+32>: mov x1, x9
0x1006e8a50 <+36>: mov w9, #0x7
0x1006e8a54 <+40>: mov x2, x9
0x1006e8a58 <+44>: bl 0x1006ed308 ; symbol stub for: swift_allocObject
0x1006e8a5c <+48>: add x8, x0, #0x10 ; =0x10
0x1006e8a60 <+52>: str x8, [sp, #0x8]
0x1006e8a64 <+56>: mov w9, #0xa
0x1006e8a68 <+60>: mov x8, x9
0x1006e8a6c <+64>: str x8, [x0, #0x10]
0x1006e8a70 <+68>: str x0, [sp]
-> 0x1006e8a74 <+72>: bl 0x1006ed3d4 ; symbol stub for: swift_retain
0x1006e8a78 <+76>: ldr x8, [sp]
0x1006e8a7c <+80>: mov x0, x8
0x1006e8a80 <+84>: bl 0x1006ed3c8 ; symbol stub for: swift_release
0x1006e8a84 <+88>: adrp x0, 2
0x1006e8a88 <+92>: add x0, x0, #0xc34 ; =0xc34
0x1006e8a8c <+96>: ldr x1, [sp]
0x1006e8a90 <+100>: ldp x29, x30, [sp, #0x10]
0x1006e8a94 <+104>: add sp, sp, #0x20 ; =0x20
0x1006e8a98 <+108>: ret
在outFunc内部调用了 swift_allocObject方法 swift_retain, swift_release
*/
func p(){
//捕获值的本质是 将变量存储在堆上
print(outFunc()())//输出:11
print(outFunc()())//输出:11
print(outFunc()())//输出:11
//内嵌函数innerFunc捕获了total,不再是单纯的一个变量
let o =outFunc()
print(o())//输出11
print(o())//输出12
print(o())//输出13
}
/*
总结:
当每次修改捕获值时,修改的是堆区中的value值
当每次重新执行当前函数时,都会重新创建内存空间
o是用于存储outFunc函数调用的全局变量,所以每次都需要依赖上一次的结果
而直接调用函数时,相当于每次都新建一个堆内存,所以每次的结果都是不关联的,即每次结果都是一致的
*/
}
网友评论