近期在某开发群里面看到一道swift的题目,你们讨论了一波,一时好奇其缘由,就本身研究了一波,再次记录一下html
var a = 1
func add() -> Int {
defer {
a = a + 1
}
return a
}
print(a) // 这里打印出来a是1?
复制代码
这里我已经把答案写出来了,print(a)
打印的是1,可为啥会是1呢?这里我先卖个关子,咱们继续往下看。git
defer
的一些理解关于defer
语句,在swift
中它被应用于什么场景呢?swift
好比,读取某目录下的文件内容并处理数据,你须要首先定位到文件目录,打开文件夹,读取文件内容以及处理数据,关闭文件以及文件夹。假若一切顺利,只需按照设定好的程序流程走一轮便可;缓存
然而,事情并不会老是如你所愿,若是中间某个环节失败,好比读取文件内容失败、处理数据失败等等,还须要进行一些后续收尾工做,即关闭文件或关闭文件夹(固然就算顺利执行,也是要关闭的)。 因为在关闭文件以前可能出现异常,致使代码没法继续往下执行,这将会致使内存泄漏,那么这时候defer
就能够来处理这种问题,将收尾工做写在defer
代码块中,保证收尾工做顺利进行。bash
以上即是我对于defer
的总体印象,我也查了下资料关于defer
的说明:less
关于swift官方文档中对于defer
的说明ide
Use defer to write a block of code that is executed after all other code in the function, just before the function returns. The code is executed regardless of whether the function throws an error. You can use defer to write setup and cleanup code next to each other, even though they need to be executed at different times.函数
SwiftGG对其也有说明学习
在 defer 语句中的语句不管程序控制如何转移都会被执行。在某些状况下,例如,手动管理资源时,好比关闭文件描述符,或者即便抛出了错误也须要执行一些操做时,就可使用 defer 语句。 若是多个 defer 语句出如今同一做用域内,那么它们执行的顺序与出现的顺序相反。给定做用域中的第一个 defer 语句,会在最后执行,这意味着代码中最靠后的 defer 语句中引用的资源能够被其余 defer 语句清理掉。ui
print(a)
的缘由窥探以上的这些说明不足以解释这个咱们的主题,为啥print(a)
t打印出来的是1,而不是2?
最近恰好在学习汇编,随即写了个demo,来跟一下汇编看看究竟是咋回事?
swiftLearning`main:
0x1000008d0 <+0>: pushq %rbp
0x1000008d1 <+1>: movq %rsp, %rbp
0x1000008d4 <+4>: subq $0x80, %rsp
0x1000008db <+11>: movq $0x1, 0x8ba(%rip) ; _swift_FORCE_LOAD_$_swiftCompatibility50
0x1000008e6 <+22>: movl %edi, -0x34(%rbp)
0x1000008e9 <+25>: movq %rsi, -0x40(%rbp)
-> 0x1000008ed <+29>: callq 0x100000a00 ; swiftLearning.add() -> Swift.Int at main.swift:13
0x1000008f2 <+34>: leaq 0x8a7(%rip), %rsi ; swiftLearning.a : Swift.Int
0x1000008f9 <+41>: xorl %edi, %edi
0x1000008fb <+43>: movl %edi, %ecx
0x1000008fd <+45>: movq %rsi, %rdi
0x100000900 <+48>: leaq -0x18(%rbp), %rsi
0x100000904 <+52>: movl $0x21, %edx
0x100000909 <+57>: movq %rax, -0x48(%rbp)
0x10000090d <+61>: callq 0x100000e86 ; symbol stub for: swift_beginAccess
0x100000912 <+66>: movq -0x48(%rbp), %rax
0x100000916 <+70>: movq %rax, 0x883(%rip) ; swiftLearning.a : Swift.Int
0x10000091d <+77>: leaq -0x18(%rbp), %rdi
0x100000921 <+81>: callq 0x100000e92 ; symbol stub for: swift_endAccess
0x100000926 <+86>: movq 0x6e3(%rip), %rax ; (void *)0x00007fff9cf6eb18: type metadata for Any
0x10000092d <+93>: addq $0x8, %rax
0x100000931 <+97>: movl $0x1, %edi
0x100000936 <+102>: movq %rax, %rsi
0x100000939 <+105>: callq 0x100000e50 ; symbol stub for: Swift._allocateUninitializedArray<A>(Builtin.Word) -> (Swift.Array<A>, Builtin.RawPointer)
0x10000093e <+110>: leaq 0x85b(%rip), %rcx ; swiftLearning.a : Swift.Int
0x100000945 <+117>: xorl %r8d, %r8d
0x100000948 <+120>: movl %r8d, %esi
0x10000094b <+123>: movq %rcx, %rdi
0x10000094e <+126>: leaq -0x30(%rbp), %rcx
0x100000952 <+130>: movq %rsi, -0x50(%rbp)
0x100000956 <+134>: movq %rcx, %rsi
0x100000959 <+137>: movl $0x20, %ecx
0x10000095e <+142>: movq %rdx, -0x58(%rbp)
0x100000962 <+146>: movq %rcx, %rdx
0x100000965 <+149>: movq -0x50(%rbp), %rcx
0x100000969 <+153>: movq %rax, -0x60(%rbp)
0x10000096d <+157>: callq 0x100000e86 ; symbol stub for: swift_beginAccess
0x100000972 <+162>: movq 0x827(%rip), %rax ; swiftLearning.a : Swift.Int
0x100000979 <+169>: leaq -0x30(%rbp), %rdi
0x10000097d <+173>: movq %rax, -0x68(%rbp)
0x100000981 <+177>: callq 0x100000e92 ; symbol stub for: swift_endAccess
0x100000986 <+182>: movq 0x67b(%rip), %rax ; (void *)0x00007fff9cf64ff8: type metadata for Swift.Int
0x10000098d <+189>: movq -0x58(%rbp), %rcx
0x100000991 <+193>: movq %rax, 0x18(%rcx)
0x100000995 <+197>: movq -0x68(%rbp), %rax
0x100000999 <+201>: movq %rax, (%rcx)
0x10000099c <+204>: callq 0x100000b00 ; default argument 1 of Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () at <compiler-generated>
0x1000009a1 <+209>: movq %rax, -0x70(%rbp)
0x1000009a5 <+213>: movq %rdx, -0x78(%rbp)
0x1000009a9 <+217>: callq 0x100000b20 ; default argument 2 of Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () at <compiler-generated>
0x1000009ae <+222>: movq -0x60(%rbp), %rdi
0x1000009b2 <+226>: movq -0x70(%rbp), %rsi
0x1000009b6 <+230>: movq -0x78(%rbp), %rcx
0x1000009ba <+234>: movq %rdx, -0x80(%rbp)
0x1000009be <+238>: movq %rcx, %rdx
0x1000009c1 <+241>: movq %rax, %rcx
0x1000009c4 <+244>: movq -0x80(%rbp), %r8
0x1000009c8 <+248>: callq 0x100000e56 ; symbol stub for: Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> ()
0x1000009cd <+253>: movq -0x80(%rbp), %rdi
0x1000009d1 <+257>: callq 0x100000e8c ; symbol stub for: swift_bridgeObjectRelease
0x1000009d6 <+262>: movq -0x78(%rbp), %rdi
0x1000009da <+266>: callq 0x100000e8c ; symbol stub for: swift_bridgeObjectRelease
0x1000009df <+271>: movq -0x60(%rbp), %rdi
0x1000009e3 <+275>: callq 0x100000e8c ; symbol stub for: swift_bridgeObjectRelease
0x1000009e8 <+280>: xorl %eax, %eax
0x1000009ea <+282>: addq $0x80, %rsp
0x1000009f1 <+289>: popq %rbp
0x1000009f2 <+290>: retq
swiftLearning`add():
0x100000a00 <+0>: pushq %rbp
0x100000a01 <+1>: movq %rsp, %rbp
0x100000a04 <+4>: subq $0x30, %rsp
0x100000a08 <+8>: leaq 0x791(%rip), %rdi ; swiftLearning.a : Swift.Int
0x100000a0f <+15>: xorl %eax, %eax
0x100000a11 <+17>: movl %eax, %ecx
0x100000a13 <+19>: leaq -0x18(%rbp), %rdx
0x100000a17 <+23>: movl $0x20, %esi
0x100000a1c <+28>: movq %rsi, -0x20(%rbp)
0x100000a20 <+32>: movq %rdx, %rsi
0x100000a23 <+35>: movq -0x20(%rbp), %r8
0x100000a27 <+39>: movq %rdx, -0x28(%rbp)
0x100000a2b <+43>: movq %r8, %rdx
0x100000a2e <+46>: callq 0x100000e86 ; symbol stub for: swift_beginAccess
0x100000a33 <+51>: movq 0x766(%rip), %rax ; swiftLearning.a : Swift.Int
0x100000a3a <+58>: movq -0x28(%rbp), %rdi
0x100000a3e <+62>: movq %rax, -0x30(%rbp)
0x100000a42 <+66>: callq 0x100000e92 ; symbol stub for: swift_endAccess
-> 0x100000a47 <+71>: callq 0x100000a60 ; $defer #1 () -> () in swiftLearning.add() -> Swift.Int at <compiler-generated>
0x100000a4c <+76>: movq -0x30(%rbp), %rax
0x100000a50 <+80>: addq $0x30, %rsp
0x100000a54 <+84>: popq %rbp
0x100000a55 <+85>: retq
swiftLearning`$defer #1 () in add():
0x100000a60 <+0>: pushq %rbp
0x100000a61 <+1>: movq %rsp, %rbp
0x100000a64 <+4>: subq $0x60, %rsp
0x100000a68 <+8>: leaq 0x731(%rip), %rax ; swiftLearning.a : Swift.Int
0x100000a6f <+15>: xorl %ecx, %ecx
0x100000a71 <+17>: movq %rax, %rdi
0x100000a74 <+20>: leaq -0x18(%rbp), %rsi
0x100000a78 <+24>: movl $0x20, %edx
0x100000a7d <+29>: callq 0x100000e86 ; symbol stub for: swift_beginAccess
0x100000a82 <+34>: movq 0x717(%rip), %rax ; swiftLearning.a : Swift.Int
0x100000a89 <+41>: leaq -0x18(%rbp), %rdi
0x100000a8d <+45>: movq %rax, -0x38(%rbp)
0x100000a91 <+49>: callq 0x100000e92 ; symbol stub for: swift_endAccess
-> 0x100000a96 <+54>: movq -0x38(%rbp), %rax
0x100000a9a <+58>: incq %rax
0x100000a9d <+61>: seto %r8b
0x100000aa1 <+65>: movq %rax, -0x40(%rbp)
0x100000aa5 <+69>: movb %r8b, -0x41(%rbp)
0x100000aa9 <+73>: jo 0x100000af0 ; <+144> at main.swift:15:15
0x100000aab <+75>: leaq 0x6ee(%rip), %rdi ; swiftLearning.a : Swift.Int
0x100000ab2 <+82>: xorl %eax, %eax
0x100000ab4 <+84>: movl %eax, %ecx
0x100000ab6 <+86>: leaq -0x30(%rbp), %rdx
0x100000aba <+90>: movl $0x21, %esi
0x100000abf <+95>: movq %rsi, -0x50(%rbp)
0x100000ac3 <+99>: movq %rdx, %rsi
0x100000ac6 <+102>: movq -0x50(%rbp), %r8
0x100000aca <+106>: movq %rdx, -0x58(%rbp)
0x100000ace <+110>: movq %r8, %rdx
0x100000ad1 <+113>: callq 0x100000e86 ; symbol stub for: swift_beginAccess
0x100000ad6 <+118>: movq -0x40(%rbp), %rcx
0x100000ada <+122>: movq %rcx, 0x6bf(%rip) ; swiftLearning.a : Swift.Int
0x100000ae1 <+129>: movq -0x58(%rbp), %rdi
0x100000ae5 <+133>: callq 0x100000e92 ; symbol stub for: swift_endAccess
0x100000aea <+138>: addq $0x60, %rsp
0x100000aee <+142>: popq %rbp
0x100000aef <+143>: retq
0x100000af0 <+144>: ud2
复制代码
上面的代码中主要分为三部分,main
,add()
,defer
,其中main
就不用多说了,add()
就是咱们题目中定义的func add(){}
的汇编,而defer
则是func add(){}
中的defer
所对应的汇编实现了,主要的函数调用步骤我分为如下几步说明
main
中,咱们看到1
被赋值到全局变量区0x1000011A0
;main函数中
0x1000008db <+11>: movq $0x1, 0x8ba(%rip) ; _swift_FORCE_LOAD_$_swiftCompatibility50 // 将1存放到全局变量区,地址为0x1000011A0
0x1000008ed <+29>: callq 0x100000a00 ; swiftLearning.add() -> Swift.Int at main.swift:13 这里就要调用到add()方法中
复制代码
callq 0x100000a00
来到add()
函数中,其中的重点部分解读以下;add函数中
0x100000a82 <+34>: movq 0x717(%rip), %rax ; swiftLearning.a : Swift.Int// 将全局变量取出来赋值给rax,能够看到此处的0x717(%rip)就是地址0x717+0x100000a89=0x1000011A0,也就是刚才存起来的1
0x100000a89 <+41>: leaq -0x18(%rbp), %rdi
0x100000a8d <+45>: movq %rax, -0x38(%rbp) ;将rax中的地址取出来,传给内存区域-0x38(%rbp)
0x100000a33 <+51>: movq 0x766(%rip), %rax ; swiftLearning.a : Swift.Int 将全局变量取出来赋值给rax,能够看到此处的0x766(%rip)就是地址0x766+0x100000a3a=0x1000011A0,也就是刚才存起来的1
0x100000a3a <+58>: movq -0x28(%rbp), %rdi
0x100000a3e <+62>: movq %rax, -0x30(%rbp) ;这一步将1放在缓存-0x30(%rbp) 中
0x100000a42 <+66>: callq 0x100000e92 ; symbol stub for: swift_endAccess
-> 0x100000a47 <+71>: callq 0x100000a60 ; $defer #1 () -> () in 这里调用defer函数
复制代码
defer函数
0x100000a91 <+49>: callq 0x100000e92 ; symbol stub for: swift_endAccess
0x100000a96 <+54>: movq -0x38(%rbp), %rax ; 将内存区域-0x38(%rbp) 中的地址去取来,再传给rax,此时rax中的地址是0x1000011A0
0x100000a9a <+58>: incq %rax ; 将rax寄存其中的值自增
复制代码
0x100000a33 <+51>: movq 0x766(%rip), %rax ; swiftLearning.a : Swift.Int 这里将全局区0x1000011A0的地址赋值给了rax寄存器
0x100000a3a <+58>: movq -0x28(%rbp), %rdi
0x100000a3e <+62>: movq %rax, -0x30(%rbp) ; 这里将rax寄存器的值付给了内存区域-0x30(%rbp)保存起来
0x100000a42 <+66>: callq 0x100000e92 ; symbol stub for: swift_endAccess
-> 0x100000a47 <+71>: callq 0x100000a60 ; $defer #1 () -> () in swiftLearning.add() -> Swift.Int at <compiler-generated>这里调用defer 这一步执行完之后,defer中的代码就调用完了
0x100000a4c <+76>: movq -0x30(%rbp), %rax ; 这里又给rax赋值了,而这里的-0x30(%rbp)缓存区放的值就是在defer调用以前存入的0x1000011A0,自此rax又被赋值为地址0x1000011A0
0x100000a50 <+80>: addq $0x30, %rsp
0x100000a54 <+84>: popq %rbp
0x100000a55 <+85>: retq ; 这里才把add()方法执行完,并return回去
复制代码
add
执行完之后直接将rax
的地址当作返回值的地址,返回并print
,此时就获得了那个1,至关于饶了一大圈,其实rax
的值仍是取的0x1000011A0
,并无拿defer
中自增操做后的值到这里咱们能够看到,虽然defer
中执行了a = a + 1
,可是在add
函数return
以前,rax
又被赋值0x1000011A0
,而在函数调用前,咱们已经看到0x1000008db <+11>: movq $0x1, 0x8ba(%rip)
,能够获得咱们在add
执行完以后,返回值仍是0x1000011A0
,解释了为啥print
打印出来仍是1的缘由
(lldb) register read rax
rax = 0x0000000000000001
1
复制代码
这里总结一下,虽然defer
是在函数返回以前会执行,可是里面的操做并不会影响返回值,返回值在defer
执行完以后,又去取了原来的值,因此print
的值仍是1