JHHK

欢迎来到我的个人网站
行者常至 为者常成

7、闭包表达式(二)

目录

函数汇编分析

/// 一、函数汇编分析
func functionAnalyInAssembly() {
    
    // 0x100007570
    func sum (_ v1:Int,_ v2:Int)->Int{
        v1+v2//0x100006d20 0x100006d72
    }
    
    var fn = sum
    print(MemoryLayout.size(ofValue: fn))//16
    print(Mems.ptr(ofVal: &fn))//0x00007ffeefbff3d0
    print(Mems.memStr(ofVal: &fn))//0x0000000100007770 0x00000001007062d0
    
    var sumValue = fn(1,2)
    print(MemoryLayout.size(ofValue: sumValue))//8
    print("sumValue = ",sumValue)//3
    print(Mems.ptr(ofVal: &sumValue))//0x00007ffeefbff3b0
    print(Mems.memStr(ofVal: &sumValue))//0x0000000000000003
    
    
    
    //    分析fn的内存分配情况
    
    //    查看fn内存储的值 0x100004d20 位于代码段
    //    (lldb) p fn
    //    () -> () $R0 = 0x0000000100004d20 LCClientDemo-commond`sum #1 (Swift.Int, Swift.Int) -> Swift.Int in LCClientDemo_commond.functionAnaly() -> () at 6-02-闭包.swift:23
    
    
    //    汇编查看计算fn变量的地址
    //    (lldb) register read rbp
    //         rbp = 0x00007ffeefbff5e0
    //    (lldb) p/x 0x00007ffeefbff5e0 - 0x10
    //    (Int) $R2 = 0x00007ffeefbff5d0
    
    
    //    查看fn对用的16字节内存存储的内容 前8字节存储的是函数sum的地址 后8字节存储 0x00
    //    (lldb) memory read/2xg 0x00007ffeefbff5d0
    //    0x7ffeefbff5d0: 0x0000000100004d20 0x0000000000000000
}

闭包汇编分析

/// 二、闭包汇编分析
func closureAnalyInAssembly() {
    
    
    //1、未捕获变量时汇编分析
    do{
        print("-----------1--------------")
        typealias Fn = (Int)->Int
        
        func getFn0()->Fn{
            //var num = 0
            func plus(_ i:Int)->Int{
                return i
            }
            return plus
        }
        
        
        //fn0的地址           plus的地址         空
        //0x7ffeefbff5c0: 0x0000000100005ae0 0x0000000000000000
        let fn0 = getFn0()
        print(MemoryLayout.size(ofValue: fn0))//16
        print(fn0(2))//2
        
        print("")
        
        /**
         plus的调用汇编,可以看出 plus的地址为 0x100005ae0
         LCClientDemo-commond`plus #1 (_:) in getFn0 #1 () in closureAnalyInAssembly():
         0x100005ae0 <+0>:  pushq  %rbp
         0x100005ae1 <+1>:  movq   %rsp, %rbp
         0x100005ae4 <+4>:  movq   $0x0, -0x8(%rbp)
         0x100005aec <+12>: movq   %rdi, -0x8(%rbp)
         0x100005af0 <+16>: movq   %rdi, %rax
         0x100005af3 <+19>: popq   %rbp
         0x100005af4 <+20>: retq
         */
    }
    
    
    
    
    //2、捕获变量时汇编分析
    do{
        print("-----------2--------------")
        typealias Fn = (Int)->Int
        
        
        func getFn()->Fn{
            var num = 0
            func plus(_ i:Int)->Int{
                num+=i
                return num
            }
            return plus
        }
        
        
        //fn占用16个字节 存储了调用plus相关的地址(不是plus的地址) 和 存储num开辟的堆空间地址
        let fn = getFn()
        //fn变量的内存大小
        print(MemoryLayout.size(ofValue: fn))//16
        
        //fn占用16个字节 前8个字节存储的是 plus的相关地址(是相关地址不是plus的地址) 后8个字节存储的是为num分配的堆空间的地址
        //   fn地址        前8字节plus函数相关地址     后8字节堆空间地址
        //0x7ffeefbff5c8: 0x0000000100005e60     0x0000000100633ad0
        print(fn(5))
        
        let fn2 = getFn()
        //  fn2地址        前8字节plus函数相关地址    后8字节堆空间地址
        //0x7ffeefbff5b0: 0x0000000100005e60     0x0000000100512370
        print(fn2(6))
        
        
        
        
        /**
         分析fn的内存分布情况:
         (lldb) p/x fn
         () -> () $R0 = 0x0000000100005e60
         
         
         (lldb) register read rax
         rax = 0x0000000100005e60  LCClientDemo-commond`partial apply forwarder for plus #1 (Swift.Int) -> Swift.Int in getFn #1 () -> (Swift.Int) -> Swift.Int in LCClientDemo_commond.closureAnalyInAssembly() -> () at <compiler-generated>
         
         
         (lldb) register read rbp
         rbp = 0x00007ffeefbff5e0
         (lldb) p/x 0x00007ffeefbff5e0 - 0x18
         (Int) $R2 = 0x00007ffeefbff5c8
         
         
         (lldb) memory read/2xg 0x00007ffeefbff5c8
         0x7ffeefbff5c8: 0x0000000100005e60 0x0000000100633ad0
         
         
         fn的调用汇编:
         ->  0x10000599e <+270>: callq  *%rcx
         (lldb) register read rcx
         rcx = 0x0000000100005e60  LCClientDemo-commond`partial apply forwarder for plus #1 (Swift.Int) -> Swift.Int in getFn #1 () -> (Swift.Int) -> Swift.Int in LCClientDemo_commond.closureAnalyInAssembly() -> () at <compiler-generated>
         
         
         call 后边一般跟一个固定值 在编译阶段 函数的地址已经确定 所以一般跟固定值
         fn是变量 内存储了 函数的地址 fn调用属于间接调用 所以后边跟的不是固定值 而是从寄存器或内存中取出的一个值
         查看汇编时 可以作为一个经验
         
         
         
         
         //可以看出0x100005e60对应的不是plus函数的地址
         LCClientDemo-commond`partial apply for plus #1 (_:) in getFn #1 () in closureAnalyInAssembly():
         ->  0x100005e60 <+0>: pushq  %rbp
         0x100005e61 <+1>: movq   %rsp, %rbp
         0x100005e64 <+4>: movq   %r13, %rsi
         0x100005e67 <+7>: popq   %rbp
         0x100005e68 <+8>: jmp    0x100005bd0
         
         
         //jmp 0x100005bd0才是调用plus方法
         //可知 plus的函数地址是 0x100005bd0 从而可以证明 fn内前8字节存储的是plus的相关地址 而不是直接存储了plus的地址
         
         LCClientDemo-commond`plus #1 (_:) in getFn #1 () in closureAnalyInAssembly():
         ->  0x100005bd0 <+0>:   pushq  %rbp
         0x100005bd1 <+1>:   movq   %rsp, %rbp
         0x100005bd4 <+4>:   subq   $0x90, %rsp
         0x100005bdb <+11>:  xorl   %eax, %eax
         0x100005bdd <+13>:  movl   %eax, %ecx
         0x100005bdf <+15>:  xorl   %eax, %eax
         0x100005be1 <+17>:  leaq   -0x8(%rbp), %rdx
         0x100005be5 <+21>:  movq   %rdi, -0x48(%rbp)
         0x100005be9 <+25>:  movq   %rdx, %rdi
         0x100005bec <+28>:  movq   %rsi, -0x50(%rbp)
         0x100005bf0 <+32>:  movl   %eax, %esi
         0x100005bf2 <+34>:  movl   $0x8, %edx
         0x100005bf7 <+39>:  movq   %rdx, -0x58(%rbp)
         0x100005bfb <+43>:  movq   %rcx, -0x60(%rbp)
         0x100005bff <+47>:  movl   %eax, -0x64(%rbp)
         */
    }
}

闭包分析 数组相关

/// 三、闭包分析 数组相关
func closureAndArray(){
    var functions:[()->Int] = []
    
    for i in 1...3 {
        //相当于下面两种写法
        functions.append{i}
        
//        let closure:()->Int = {() in return i}
//        functions.append(closure)
        
//        func fn ()->Int{
//            return i
//        }
//        functions.append(fn)
        
        /**
         闭包:
         i 是局部变量
         被函数或闭包{i}捕获
         */
    }
    
    
    
    for fn in functions {
        /**
         fn 前8个字节 函数地址相关 后8个字节堆空间地址
         */
        
        
        /**
         (lldb) p fn
         () -> () $R0 = 0x0000000100005bd0 LCClientDemo-commond`partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed () -> (@out Swift.Int) to @escaping @callee_guaranteed () -> (@unowned Swift.Int) at <compiler-generated>

         
         ->  0x100004fa0 <+1264>: callq  *%rcx

         (lldb) register read rcx
              rcx = 0x0000000100005bd0  LCClientDemo-commond`partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed () -> (@out Swift.Int) to @escaping @callee_guaranteed () -> (@unowned Swift.Int) at <compiler-generated>

         ->  0x100005bd0 <+0>:  pushq  %rbp
             0x100005bd1 <+1>:  movq   %rsp, %rbp
             0x100005bd4 <+4>:  movq   0x10(%r13), %rdi
             0x100005bd8 <+8>:  movq   0x18(%r13), %rsi
             0x100005bdc <+12>: popq   %rbp
             0x100005bdd <+13>: jmp    0x100005b80               ; reabstraction thunk helper from @escaping @callee_guaranteed () -> (@out Swift.Int) to @escaping @callee_guaranteed () -> (@unowned Swift.Int) at <compiler-generated>

         */
        print(fn())
    }
}

自动闭包

/// 四、自动闭包
func autoClosure() -> Void {
    func getFirstPositive(_ v1:Int,_ v2:()->Int)->Int{
        return v1 > 0 ? v1 : v2()
    }


    let first = getFirstPositive(10) { () -> Int in
        20
    }
    print(first)

    let first1 = getFirstPositive(-1) { () -> Int in
        20
    }
    print(first1)


    
    
    func getFirstPositive1(_ v1:Int,_ v2:@autoclosure ()->Int)->Int{
        return v1 > 0 ? v1 : v2()
    }

    let first2 = getFirstPositive1(10, 20)
    print(first2)
    
    let first3 = getFirstPositive1(-1, 20)
    print(first3)
}

测试

/// 五、测试6
func test6() -> Void {
    
    func add(_ num:Int)->(inout Int)->Void{
        
        func plus(v:inout Int)->Void{
             v += num
        }
        
        return plus
    }
    
    var v = 5
    add(20)(&v)
    print(v)
}

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.