美文网首页
闭包(底层捕获原理)

闭包(底层捕获原理)

作者: 吕建雄 | 来源:发表于2021-08-04 15:17 被阅读0次

    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函数调用的全局变量,所以每次都需要依赖上一次的结果

         而直接调用函数时,相当于每次都新建一个堆内存,所以每次的结果都是不关联的,即每次结果都是一致的

         */

    }

    相关文章

      网友评论

          本文标题:闭包(底层捕获原理)

          本文链接:https://www.haomeiwen.com/subject/mdyuvltx.html