近期在某開發羣裏面看到一道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