“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
- modified
Swift
Function type (callC
Function.) - call
OC
Method, modifySwift
Function 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