Recently, we saw a swift topic in a development group, and we discussed a wave of it. For a while, we were curious about the reason, so we studied a wave and recorded it again

var a = 1
func add() -> Int {
    defer {
        a = a + 1
    }
    return a
}
print(a) // Here print out a is 1?Copy the code

Print (a) says 1, but why does it say 1? I’m going to keep you in suspense. Let’s move on.

deferSome understanding of

aboutdeferStatements, inswiftWhat is it used in?

For example, to read the contents of a file in a directory and process the data, you need to locate the file directory, open the folder, read the contents of the file and process the data, and close the file and folder. If all goes well, just follow the set procedure one round;

However, things don’t always go your way, and if something fails, such as reading the contents of the file, processing the data, etc., there are some finishing touches that need to be done to close the file or close the folder (even if it works, of course). Since an exception may occur before the file is closed and the code can’t proceed, which will result in a memory leak, defer can handle this by putting the wrap up in the defer block to make sure the wrap up goes smoothly.

The above is my overall impression of defer. I have also checked the information about the explanation of defer:

Please refer to the description of defer in swift official documentation

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 also has instructions for this

The statements in the DEFER statement will be executed regardless of how program control is transferred. You can use the defer statement in some cases, for example, when you manage resources manually, such as closing file descriptors, or when you need to do something even if an error is thrown. If multiple defer statements appear in the same scope, they will be executed in the opposite order as they appear. The first defer statement in a given scope is executed last, which means that resources referenced in the last defer statement in the code can be cleaned up by other defer statements.

In the titleprint(a)The cause of snooping

Why print(a)t prints 1 instead of 2?

Recently I happened to learn assembly, and then wrote a demo, to follow the assembly to see what is going on?


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 forSwift.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 stubfor: 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    
  

Copy the code

The above code is mainly divided into three parts: main, add() and defer. There is no need to talk about main. Add () is the assembly of func add(){} defined in the title, while defer is the assembly implementation corresponding to the defer in func add(){}. The main function call steps are described in the following steps

  1. inmainIn, we see that1Is assigned to the global variable area0x1000011A0
; 0x1000008DB <+11>: movQ in main$0x1, 0x8ba(%rip)         ; _swift_FORCE_LOAD_$_swiftCompatibility50// Store 1 in the global variable area at 0x1000011A0 0x1000008ed <+29>: callq 0x100000a00; Swiftlearning.add () -> swift.int at main. Swift :13Copy the code
  1. I’m going to talk aboutcallq 0x100000a00Came toadd()Function, the key part of which is interpreted as follows
; Add 0x100000a82 <+34>: movq 0x717(%rip), %rax; swiftLearning.a : Swift.Int// remove the global variable and assign it to rax, you can see that 0x717(%rip) is the address 0x717+0x100000a89=0x1000011A0, that is, 1 0x100000a89 <+41>: leaq -0x18(%rbp), %rdi 0x100000a8d <+45>: movq %rax, -0x38(%rbp) ; -0x38(% RBP) 0x100000a33 <+51>: movq 0x766(%rip), %rax; swiftLearning.a : Swift.Int = 0x766+0x100000a3a=0x1000011A0 Swift.Int = 0x766+0x100000a3a <+58> movq -0x28(%rbp), %rdi 0x100000a3e <+62>: movq %rax, -0x30(%rbp) ; This step places 1 in cache -0x30(% RBP) 0x100000a42 <+66>: callq 0x100000e92; symbol stubfor: swift_endAccess
->  0x100000a47 <+71>: callq  0x100000a60               ; $defer #1 () -> () in Calls the defer function here
    
Copy the code
  1. By the time we got to defer, we had already called itDefer function
   
    0x100000a91 <+49>:  callq  0x100000e92               ; symbol stub for: swift_endAccess 0x100000a96 <+54>: movq -0x38(%rbp), %rax ; Remove the address from memory area -0x38(% RBP) and pass it to RAX. At this time, the address in RAX is 0x1000011A0 0x100000a9A <+58>: INCq %rax; The value stored in RAX increasesCopy the code
  1. The function defer knows ret in the code from now on and has not operated on rax again. By this point in the code, we have seen that a = A + 1 in defer, but is that all? Let’s move on to the key code
0x100000a33 <+51>: movq 0x766(%rip), %rax ; Swiftlearning. a: swiftlearning. Int Assigns the address of global area 0x1000011A0 to raX register 0x100000a3a <+58>: MOVq-0x28 (% RBP), %rdi 0x100000a3e <+62>: movq %rax, -0x30(%rbp) ; Here the raX register value is paid to the memory area -0x30(% RBP) save 0x100000a42 <+66>: callq 0x100000e92; symbol stubfor: swift_endAccess
->  0x100000a47 <+71>: callq  0x100000a60               ; $defer #1 () -> () in swiftlearning.add () -> swift.int at 0x100000a4c <+76>: movq -0x30(%rbp), %rax ; Here rax is assigned again, and the -0x30(% RBP) cache here is the 0x1000011A0 stored before the defer call, from which RAx is assigned address 0x1000011A0 0x100000a50 <+80>: addQ$0x30, %rsp 0x100000a54 <+84>: popq %rbp 0x100000a55 <+85>: retq ; I'm just done with the add() method andreturnGo back toCopy the code
  1. whenaddDirectly after the executionraxAs the address of the return value, return andprintAnd then you get that 1, which is kind of a full circle, actuallyraxThe value of theta is still taken0x1000011A0“And did not take itdeferIs the value after the increment operation

Here we can see that although a = a +1 was implemented in defer, before the add function return, rax was assigned 0x1000011A0, and before the function call we already saw 0x1000008DB <+11>: Movq $0x1, 0x8ba(%rip), we can get that after the add execution, the return value is still 0x1000011A0, which explains why the print is still 1

(lldb) register read rax
     rax = 0x0000000000000001
1
Copy the code

To summarize, although defer was executed before the function returned, it did not affect the return value, which was retrieved after defer, so the value of print was still 1