Preface
Whether it is Apple's official documents or some articles and books derived from official documents, they attach more importance to the explanation of basic grammar knowledge, and there are very few mentions of applications in actual combat. Therefore, when we want to use "closures" to solve some problems, we will suddenly feel embarrassed to look at a bunch of theoretical knowledge but don't know where to start. This is the difference between theory and time-based practical combat.
This article will not elaborate on the basic syntax of Swift closures. There are a lot of information under Baidu or Google. As shown in the title, this article focuses on some practical cases of Swift closures. Friends who need it can refer to it. Experienced masters also ask for advice.
About how to understand closures
The first difficulty in learning closures is understanding closures. Many people may not know what closures are when they have used them for a long time. I provide an understanding idea here for reference only.
For many iOS developers, when they first come into contact with Swift closures, they will try to use Blocks in OC to understand them. Of course, this will be of some help to our understanding. For example, many people learn English: tomato->Tomato->🍅, not tomato->🍅, and when a baby first comes into contact with language, he directly associates the pronunciation of tomato to 🍅, and then learns the spelling of 🍅. This is the logical process of learning language that humans are born with, so we might as well learn and understand Swift closures according to this thinking logic.
1. What is a closure
It's like a baby is curious about what a tomato is. Parents will take a real tomato to him, look, touch, smell, and taste it. For Swift's closure, we don't need to know how it is syntactically defined, but rather the nature of the closure.
The essence of a closure is a code block, which is an upgraded version of a function. A function is a code block with a name and reusable, while a closure is an anonymous code block that is more flexible than a function.
2. Why do you need a closure?
When a baby knows what tomatoes are, he will naturally think of what the use of tomatoes is. Then we will naturally ask what the use of closures is in Swift?
Functions can already meet most of our development needs, so why do we still need closures? In development, we often need to pass various data. We are used to passing a value: Int, a string of symbols: String, an object: Class, but sometimes we need to pass a logic to deal with problems. The types we use seem to not meet this requirement, and functions happen to be a logic to deal with problems. In order to make functions pass and call as flexibly as commonly used types such as Int, Float, String, etc., closures appear.
To sum up, we can know that closures are essentially code blocks like functions, while closures are more flexible.
Closures, nested functions, functions
Before using closures better, you need to clarify the connections and differences between the three people.
First look at the definitions of three functions:
//functionfunc eatTomatos(a: Int, b: Int) -> Int { return a + b } //Nested functionsfunc eatTomatos(a: Int, b: Int) -> Int { //Nested functions func digest(a: Int, b: Int) -> Int { return 2 * a + b } return digest(a: a, b: b) } //Closingvar eatTomatos = {(a: Int, b: Int) -> Int in return a + b }
From the above definition, we can see that functions and nested functions are actually the same thing. The only difference is that nested functions are functions defined inside a function, hidden from the outside and can only be valid inside the function defined. The difference between closures and functions is more: 1. There is no need to use the func keyword. 2. Secondly, the function has a name such as eatTomatos, while the closure has no name. 3. The parameters and function body of the closure must be wrapped with { }. After the parameters, the in keyword must be used to connect the function body. 4. The closure can be assigned as a type to a variable. The closure type in the above code is: (Int, Int) -> Int
。
The above analyzes the differences between the three in terms of definition, and the following distinguishes them from the functional level.
1. Functions are global and cannot capture variables in the context; while nested functions and closures can be directly nested in the context, so variables in the context can be captured. It should be noted that each closure will only hold a copy of the variables it captures, as follows:
override func viewDidLoad() { () print(eatTomatos(a: 1, b: 2))//③ print(eatTomatos(a: 2, b: 3))//④ } func eatTomatos(a: Int, b: Int) -> Int { var numArray: Array<Int> = () //Nested functions func digest(a: Int, b: Int) -> Int { (a) (b) print()//② return 2 * a + b } print()//① return digest(a: a, b: b) } //The printing results are:0 2 4 0 2 7
2. The closure can be used as a parameter or return value, as follows:
// as parameteroverride func viewDidLoad() { () cookTomates { (a, b) in print(a) print(b) } } func cookTomates(tomato: (Int, Int) -> Void){ tomato(1, 2) }
The cookTomates function will be closed(Int, Int) -> Void
As a parameter, this closure can be operated inside the function
When calling cookTomates functional formula, you need to assign a value to this closure parameter, and the parameter name in the closure needs to be called, it is named by itself.
//As return valueoverride func viewDidLoad() { () let tomato = gainTomatos() print(tomato(2, 3)) } var eatTomatos: (Int, Int) -> Int = {(a: Int, b: Int) -> Int in return a + b } func gainTomatos() -> (Int, Int) -> Int { return eatTomatos }
The function gainTomatos will closure(Int, Int) -> Int
As the return value, what is returned here is(Int, Int) -> Int
An instance of the caller can obtain the returned instance(Int, Int) -> Int
Logic of closure processing parameters to realize code passing and multiplexing
Alias your closure type
The closure type does not look concise like other commonly used types. It has parameters, return values, keywords, and symbols, which affect reading and error correction. Therefore, it is necessary to give an alias for the commonly used closure type.
As follows,(Int, Int) -> Int
Alias the closure type
typealias Tomato = (Int, Int) -> Int
Therefore, the code used as the return value of the above closure can be rewritten as follows:
override func viewDidLoad() { () let tomato = gainTomatos() print(tomato(2, 3)) } var eatTomatos: Tomato = {(a: Int, b: Int) -> Int in return a + b } func gainTomatos() -> Tomato { return eatTomatos }
When we put(Int, Int) -> Int
After the type abstraction is Tomato, not only the code looks more concise, but also closer to other parameter types we use, making it easier to understand.
Closure value transfer
Commonly used value transfer methods in OC include proxy, block, notification, etc., and the corresponding Swift Block is replaced by closures.
The following is required to use a closure to pass the values a and b in B into A
override func viewDidLoad() { () let a: A = A() () } typealias Tomato = (Int, Int) -> Int class A: NSObject { let b: B = B() func fromB() { = { (x, y) -> Int in return x + y } print(()) } } class B: NSObject { var tomato: Tomato? func toA() -> Int { let a = 3 let b = 4 return tomato!(a, b) } }
From the above, we can summarize the process of closure transfer:
1️. First give an alias for your closure type, which is easy to use;
2️. Declare a closure type variable in a class that needs to pass the value to another object, corresponding to the above code is B;
3️ Assign values to the closure type in the class that needs to receive the value, so that the passed value can be obtained in this closure.
Notice:
Here we focus on describing the process of passing values. During development, we also need to determine whether the closure is nil, otherwise it will cause a crash;
Pass the value as a parameter
When using AFN or SDWebImage, it is very convenient to obtain the requested data through Block. So how to use closures to achieve this effect in Swift.
In fact, when we are talking about the use of closures as parameters, we have implemented this way of passing values. Here is another example. When we use third-party libraries, we usually encapsulate them again to avoid unnecessary workload when third-party libraries are not maintained or large updates occur. Here we take the simple encapsulation of Alamofire as an example, the code is as follows:
import UIKit import Alamofire import SwiftyJSON class ZYLResponse: NSObject { //Is the data received successful? var isSuccess: Bool = false //Received dictionary data var dict: Dictionary<String, Any>? //Received array data var array: Array<Any>? //error message var error: Error? //JSON var json:JSON? } typealias DataReply = (ZYLResponse) -> Void class ZYLNetTool: NSObject { ///POST request open static func post(url: String, parameters: Dictionary<String, Any>?, complete: @escaping DataReply) { (url, method: .post, parameters: parameters).responseJSON { (response) in let myResponse = ZYLResponse() = = as! Dictionary<String, Any>? = as? Array<Any> = = JSON(data: !) complete(myResponse) } } ///GET request open static func get(url: String, parameters: Dictionary<String, Any>?, complete: @escaping DataReply) { (url, method: .get, parameters: parameters).responseJSON { (response) in let myResponse = ZYLResponse() = = as! Dictionary<String, Any>? = as? Array<Any> = = JSON(data: !) complete(myResponse) } } } //Call (url: HTTP_ACTIVATE_PORT, parameters: paraDict, complete: { (response) in if { //Request data successfully } else { //Request data failed } })
Notice:
1. Pay attention to managing memory when using closures;
2. When used as a closure as a function parameter, it can be used independently from the function. Declare this closure as an escape closure and add @escaping before the parameter type, otherwise an error will be reported.
Summarize
Before we come into contact with a new thing, we always find it difficult. When we learn it, we find that it is actually very simple. The difficulty is not the new thing itself, but the difficulty of our brains is difficult to accept new things out of habit, and it always requires a certain process. I remember it is difficult to understand pointers when learning C language, it is difficult to understand object-oriented when learning C++, and it is difficult to understand Block when learning OC. As a new language, Swift will inevitably have many new things that are difficult for us to understand, such as closures, tuples, optional types, functional programming, etc. The above is the entire content of this article. This article only expresses a little humble opinion on closures. If there are any shortcomings, I hope to correct them. Thank you.