“This is the third day of my participation in the First Challenge 2022, for more details: First Challenge 2022”.

OC blocks and Swift closures call each other

How is the Block that we defined in OC called in Swift? Let’s see

typedef void(^ResultBlock)(NSError *error); 

@interface LGTest : NSObject

+ (void)testBlockCall:(ResultBlock)block; 

@end
Copy the code

We can use this in Swift

LGTest.testBlockCall{ error in
    let errorcast = error as NSError
    print(errorcast) 
}

func test(_ block: ResultBlock){
    let error = NSError.init(domain: NSURLErrorDomain, code: 400, userIn: nil) 
    block(error)
}
Copy the code

For example, we defined it this way in Swift, and it can also be used in OC

class LGTeacher: NSObject{ @objc static var closure: (() -> ())? } + (void)test{ // LGTeacher.test{} LGTeacher.closure = ^{ NSLog(@"end"); }; } LGTest.test() LGTeacher.closure! (a)Copy the code

@convention Keyword description

@convention: Used to modify function types

  • modifiedSwiftFunction type (callCFunction.)
  • callOCMethod, modifySwiftFunction types

Defer the usage of the

Definition: The code in defer {} will be executed when the current code block returns, regardless of which branch it returned from, even if the application throws an error.

  • Case 1

If multiple defer statements appear in the same scope, they appear in the reverse order of their execution, i.e., first and then executed.

  • Case 2
func append(string: String, terminator: String = "\n", toFileAt url: URL) throws { // The data to be added to the file let data = (string + terminator).data(using: .utf8)! let fileHandle = try FileHandle(forUpdating: url) defer { fileHandle.closeFile() } // If file doesn't exist, create it guard FileManager.default.fileExists(atPath: url.path) else { try data.write(to: url) return } // If file already exists, append to it fileHandle.seekToEndOfFile() fileHandle.write(data) } let url = URL(fileURLWithPath: NSHomeDirectory() + "/Desktop/swift.txt") try AppEnd (string: "iOS development language ", toFileAt: url) try Append (string: "Swift", toFileAt: url)Copy the code

Sometimes here we can use defer if closeFile appears multiple times in the current method.

  • Case 3
let count = 2
let pointer = UnsafeMutablePointer<Int>.allocate(capacity: count)
pointer.initialize(repeating: 0, count: count)
defer {
    pointer.deinitialize(count: count)
    pointer.deallocate()
}
Copy the code

When we used the pointer, allocate and Deallocate and initialize and Deinitialize were paired together, so we could put deallocate and DeInitialize into defer {}.

  • Case 4
func netRquest(completion: () -> Void) {
    defer {
        self.isLoading = false
        completion()
    }
    guard error == nil else { return }
}
Copy the code

For example, when we are making a network request and there may be different branches executing the callback function, we can use defer {} to manage the completion callback, provided that it is non-asynchronous and defer {} is convenient for us to manage the code block.

Please defer to me.

Avoid writing like this when you use defer, and the compiler will indicate that defer may not be executed immediately.

The important thing to note here is that the value of temp is still 1, as defer {} is executed after the return. In fact, it doesn’t make sense to use defer like this. We need to avoid writing it like this. We need to manage some unified resources to optimize the redundant code and make the code more concise and intuitive.

Escape a closure

Escape closure: When a closure is passed to a function as an actual argument and is called after the function returns, the closure is said to have escaped. When we declare a function that accepts closures as formal arguments, you can make it clear that closures are allowed to escape by preceded the formal argument with @escaping.

  • Passed as an argument to a function
  • The current closure is executed asynchronously or stored inside the function
  • The function ends, the closure is called, and the life cycle ends

Note: Optional types default to escape closures!

  • Usage Scenario 1
class CXTeacher {
    var completionHandle: ((Int) -> Void)?
    
    func makeIncrementer(_ amout: Int, handler: @escaping (Int) -> Void) {
        var count = 10
        count += amout
        
        completionHandle = handler
    }
    
    func dosomething() {
        makeIncrementer(20) {
            print($0)
        }
    }
}

let t = CXTeacher()
t.dosomething()
t.completionHandle?(10) 
Copy the code

Here we define a completionHandle property, assigning the closure parameter handler to the completionHandle in the makeIncrementer function, The closure’s lifetime is longer than the makeIncrementer function’s, and we can call the closure when we need it, in which case the closure is the escape closure.

  • Usage Scenario 2
class CXTeacher { var completionHandle: ((Int) -> Void)? func makeIncrementer(_ amout: Int, handler: @escaping (Int) -> Void) { var count = 10 count += amout DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) {handler(count)}} func dosomething() {makeIncrementer(20) {print($0)}} let t = CXTeacher() t.dosomething() t.completionHandle?(10)Copy the code

The closure is executed asynchronously in the original function, and the closure’s lifetime is longer than the original function, and the handler is also an escape closure.

Non-escape closure

func testNoEscaping(_ f: () -> Void) {
    f()
}

func test() -> Int {
    var age = 18
    testNoEscaping {
        age += 20
    }

    return 30
}

test()
Copy the code

This is a non-escape closure that disappears when the function is called. And non-escape closures have the following advantages:

  • No circular reference is generated, the function is scoped out

  • Compiler will do more performance optimizations (retain, relsase)

  • Context memory is stored on the stack, not on the heap