“This is the 16th day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

1. defer

The definition of defer{} : The functions in defer are executed after the function completes or returns, even if an exception is thrown. Here’s a simple example:

func f() {

    defer { print("First defer") }

    defer { print("Second defer") }

    print("End of function")

}

f()
Copy the code

If multipledefer Statements occur in the same scope in the order in which they are executedReverse orderThat is, what comes first, comes later. The execution result

  • returnIn the case
func test(){

    defer {

        print(#function)}guard false else{

        return

    }

}


test()
Copy the code

If deferred {} had been placed under the judgment, it would have returned and not executed

We can use defer{} for some uniform operations, which will make the code more elegant. For example, we can use defer when we know that we need to manually create the pointer memory space and destroy it when we don’t use it

let count = 1

let pointer = UnsafeMutablePointer<Int>.allocate(capacity: count)

pointer.initialize(repeating: 0.count: count)


defer {

    pointer.deinitialize(count: count)

    pointer.deallocate()

}
Copy the code

Or when a network request is made, a different branch of the callback function may execute

func netRquest(completion: () -> Void) {

defer {

//self.isLoading = false

completion()

    

}

let requestStatus = false

    

guard requestStatus == true else {

    print("requestStatus 400")

    return}}Copy the code

2. Capture two or more variables

We looked at the case where a closure captures a variable, so if the closure is an object

class Person {

    var age = 10

}


func test() {

    

    let p = Person()

    

    let closure = {

        

        p.age += 1

    }

    closure()

}

test()
Copy the code

Let’s look at the IR code

Instead of creating the instance variable P again in the heap, we get the address of P and store it in the closure. This process eliminates the need to capture the instance variable P to the heap. Therefore, it is only necessary to pass the reference type address when it is used.

So what happens in multiple cases

func makeIncrementer() -> (_ amount:Int) -> Int {


var runningTotal = 10

var  a = 1

func incrementer(_ amount:Int) -> Int {


runningTotal += 1

           a += 1

runningTotal += a

runningTotal += amount

return runningTotal


}

return incrementer



}

let makeInc = makeIncrementer()

print(makeInc(1))

Copy the code

Let’s add two variables and look at the IR code

After capturing multiple values, the corresponding values are called several times swift_allocObjectMethods. The first and second values store variables of type Int. The third time the returned instance is cast to a structure pointer:<{ %swift.refcounted, %swift.refcounted*, %swift.refcounted* }>*And then passedgetelementptrStores the created structure, that is, the captured variables, to% 13This structure right here.

We use the structure for reduction

func makeIncrementer() -> (_ amount: Int) -> Int{

    var runningTotal = 1

    var b = 2

    // An inline function, also a closure

    func incrementer(_ amount: Int) -> Int{

        runningTotal += amount/ / 21

        b += 2;/ / 4

        runningTotal += b/ / 25

        return runningTotal

    }

    return incrementer

}
// Data structure: the execution address of the closure + the address of the capture variable heap space
struct ClosureData<T>{         
var ptr: UnsafeRawPointer     
var object: UnsafePointer<T> 
}


// Capture the value of the box

struct TowParamsClosureData<T1, T2>{

    var object: HeapObject

    var value1: UnsafePointer<Box<T1>> // The first variable

    var value2: UnsafePointer<Box<T2>>  // The second variable

}
struct HeapObject{

    var metadata: UnsafeRawPointer

    var refcount1: Int32

    var refcount2: Int32

}


struct Box<T>{

    var object: HeapObject

    var value: T

}


struct NoMeanStruct{

    var f: (Int) -> Int

}
var f = NoMeanStruct(f: makeIncrementer())

f.f(20)


let ptr = UnsafeMutablePointer<NoMeanStruct>.allocate(capacity: 1)

ptr.initialize(to: f)


let ctx = ptr.withMemoryRebound(to: ClosureData<TowParamsClosureData<Int, Int>>.self, capacity: 1){

    $0.pointee

}


print(ctx.ptr)

print(ctx.capatureValue.pointee.value1.pointee.value)

print(ctx.capatureValue.pointee.value2.pointee.value)
Copy the code

Print the result

When we capture 3 variables, it’s like we add value3 to the box and print

The amount is not captured; it is an instance object property, like our block parameter, and does not increase the reference count.

  • Change the position of the parameter

We change the position of amount to an external function argument that is not a closure function

Let’s change the structure type and get rid of the closure type

We print the captured variable runningTotal and the passed parameter 100.

Let’s just print CTX

3. Summary

When capturing multiple values, the captured variables are placed in a structure’s boxValues, which are then stored in the closure’s structure. Parameters to the closure are not captured and are equivalent to attributes of the closure object. Closures do not capture heap objects, only refer to the address of the heap object.