“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 order
That is, what comes first, comes later. The execution result
return
In 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_allocObject
Methods. 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 passedgetelementptr
Stores the created structure, that is, the captured variables, to% 13
This 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.