The practical case of closures in Swift is explained in detail

  • 2020-05-27 07:17:37
  • OfStack

preface

Whether apple's official documentation or derived by official document 1 some articles and books are attach importance to the interpretation of basic grammar knowledge, for practical application are rarely mentioned, so when we want to use a "closure", to solve the problem of some 1 will appear suddenly looked at a pile of theoretical knowledge but don't know where to lay their hands on the embarrassment of feeling, this is the difference between the theory and actual combat.

The basic syntax of the Swift closure is not covered in this article. There is a lot of information available under baidu or Google. As shown in the title, this article focuses on some practical cases of Swift closure. Those who need it can refer to it.

On how to understand closures

The first difficulty in learning closures is to understand them. Many people may have been using closures for a long time and have no idea what a closure is.

For many iOS developers, the first Swift closure will attempt to be understood using Block in OC, which will certainly help our understanding, just as many people learn English: tomato- > Tomatoes - > The & # 127813; Instead of tomato- > The & # 127813; When a baby is first exposed to language, it is directly associated with the pronunciation of tomato. And then go on to learn. This is the logical process of language learning that humans are born with, so we might as well learn and understand the closure of Swift in accordance with this logic of thinking.

1. What is a closure

This is like a baby curious about what a tomato is, the parents will take a real tomato in front of him, look 1 look, touch 1 touch, smell 1 smell, taste 1. For the closure of Swift, we first need to know not how it is syntactically defined, but the nature of the closure.

The essence of a closure is a block of code, an upgraded version of a function that is a named, reusable block of code, and a closure is an anonymous block of code that is more flexible than a function.

2. Why do you need closures

When a baby knows what a tomato is, it's natural to think about what a tomato can do, so it's natural to ask what closures can do with Swift.

Functions already meet most of our development needs, so why do we need closures? In development, we often need to pass a variety of data, we used to pass a value: Int, 1 series: String, one object: Class, but sometimes we need to pass a kind of logic to deal with problems, we do not commonly used type seems to meet this demand, the function is a kind of logic to deal with problems, in order to make function such as Int, Float, String commonly used type flexible transfer and call 1 samples, the closure.

To sum up, we can see that closures are essentially blocks of code like function 1, and closures are more flexible.

Closures, nested functions, functions

Before you can make better use of closures, you need to clarify the connections and differences between the three

First, let's look at the definition of three kinds of functions:


// function 
func eatTomatos(a: Int, b: Int) -> Int {
 return a + b
}

// Nested function 
func eatTomatos(a: Int, b: Int) -> Int {
 // Nested function 
 func digest(a: Int, b: Int) -> Int {
  return 2 * a + b
 }

 return digest(a: a, b: b)
}

// closure 
var eatTomatos = {(a: Int, b: Int) -> Int in
 return a + b
}

From the above definition, we can see that functions and nested functions are actually 1 thing. The only difference of 1 is that nested functions are functions defined inside a function, hidden from the outside, and only valid inside the defined function. Closures and function more different 1:1, do not need to use func keywords, 2, the second function has a name such as: eatTomatos, there is no name and closure, 3, the parameters of the closure, and the body of the function to use {} wrapped up, after the parameters to use in keyword connection function body, four, closures can be used as one kind of type assignment to a variable, is a type of closure in the above code: (Int, Int) -> Int .

The differences between the three are analyzed by definition above, and they are differentiated by function below.

1. Functions are global and cannot capture variables in context; Nested functions and closures can be directly nested for use in the context, so variables in the context can be captured. It is important to note that each closure holds only one copy of the variables it captures, as follows:


override func viewDidLoad() {
  super.viewDidLoad()
  print(eatTomatos(a: 1, b: 2))// 3. 
  print(eatTomatos(a: 2, b: 3))// (4) 
}

func eatTomatos(a: Int, b: Int) -> Int {
 var numArray: Array<Int> = Array.init()

 // Nested function 
 func digest(a: Int, b: Int) -> Int {
  numArray.append(a)
  numArray.append(b)
  print(numArray.count)// 2. 
  return 2 * a + b
 }

 print(numArray.count)// 1. 

 return digest(a: a, b: b)
}

// The printed results () were: 
0
2
4
0
2
7

2. Closures can be used as parameters or return values, as follows:


//  As a parameter 
override func viewDidLoad() {
 super.viewDidLoad()
 cookTomates { (a, b) in
  print(a)
  print(b)
 }
}

func cookTomates(tomato: (Int, Int) -> Void){
 tomato(1, 2)
}

The cookTomates function will close (Int, Int) -> Void As a parameter, and you can manipulate the closure inside the function

When calling the cookTomates function requires assigning a value to the closure parameter and the parameter name in the closure needs to be called.


// As the return value 
override func viewDidLoad() {
 super.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 close (Int, Int) -> Int As a return value, this is what's being returned (Int, Int) -> Int The caller can get the returned instance (Int, Int) -> Int Closures handle the logic of parameters and realize the transfer and reuse of code

Alias your closure type

Closure types do not look as simple as other commonly used types. They are composed of parameters, return values, keywords, and symbols that affect reading and error correction, so it is necessary to give a name to a commonly used closure type.

As follows, (Int, Int) -> Int Closure types are aliased


typealias Tomato = (Int, Int) -> Int

So the code used for the above closure as a return value can be rewritten as follows:


override func viewDidLoad() {
 super.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 When the type is abstracted to Tomato, not only does the code look cleaner, but it is also closer to the other parameter types we use and easier to understand

Closure spread value

The commonly used value transfer methods in OC include proxy, Block, notification, etc. When corresponding to Swift, Block is replaced by a closure.

You need to use closures to pass the a and b values from B to A as follows


override func viewDidLoad() {
 super.viewDidLoad()

 let a: A = A()
 a.fromB()

}

typealias Tomato = (Int, Int) -> Int

class A: NSObject {
 let b: B = B()

 func fromB() {
  b.tomato = {
   (x, y) -> Int in
   return x + y
  }
  print(b.toA())
 }

}

class B: NSObject {
 var tomato: Tomato?

 func toA() -> Int {
  let a = 3
  let b = 4
  return tomato!(a, b)
 }

}

The value transfer process of closures can be summarized as follows:

1 the & # 65039; , first for their own closure type to give a name, easy to use;

2 the & # 65039; Declare a variable of closure type in a class that needs to pass its value to another object, which corresponds to B in the code above.

3 the & # 65039; , assign a value to the closure type in the class that needs to receive the value, so that the passed value can be obtained within the closure.

Note:

This article focuses on the process of value transfer. When developing, it is necessary to judge whether the closure is nil or it will crash.

Closures pass values as parameters

When using AFN or SDWebImage, it is convenient to get the requested data through Block, so how do you use closures in Swift to achieve this effect?

In fact, the above said in the closure as a parameter to use, has achieved this by value, here the other one example, when we were in the use of third party libraries tend to its packaging once again, avoid the third party libraries do not maintain or update considerably increase the unnecessary workload, here in a simple package Alamofire as an example, the code is as follows:


import UIKit
import Alamofire
import SwiftyJSON

class ZYLResponse: NSObject {
 // Received data successfully 
 var isSuccess: Bool = false
 // Received dictionary data 
 var dict: Dictionary<String, Any>?
 // Received array data 
 var array: Array<Any>?
 // The 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) {
  Alamofire.request(url, method: .post, parameters: parameters).responseJSON { (response) in
   let myResponse = ZYLResponse()
   myResponse.isSuccess = response.result.isSuccess
   myResponse.dict = response.result.value as! Dictionary<String, Any>?
   myResponse.array = response.result.value as? Array<Any>
   myResponse.error = response.result.error
   myResponse.json = JSON(data: response.data!)

   complete(myResponse)
  }
 }

 ///GET request 
 open static func get(url: String, parameters: Dictionary<String, Any>?, complete: @escaping DataReply) {
  Alamofire.request(url, method: .get, parameters: parameters).responseJSON { (response) in
   let myResponse = ZYLResponse()
   myResponse.isSuccess = response.result.isSuccess
   myResponse.dict = response.result.value as! Dictionary<String, Any>?
   myResponse.array = response.result.value as? Array<Any>
   myResponse.error = response.result.error
   myResponse.json = JSON(data: response.data!)

   complete(myResponse)
  }
 }

}

// call 
  ZYLNetTool.post(url: HTTP_ACTIVATE_PORT, parameters: paraDict, complete: { (response) in
   if response.isSuccess {
    // Requested data successfully 

   } else {
    // Request data failed 

   }
  })

Note:

1. Pay attention to memory management when using closures;

2. If a closure can be used independently of a function when it is used as a function parameter, declare it as an escape closure by prefacing the parameter type with @escaping, otherwise an error will be reported.

conclusion

It is always difficult to learn a new thing, but when we learn it, we find it is actually very simple. The difficulty is not the new thing itself, but our brain is hard to accept the new thing out of habit, which always needs a definite process. Is difficult to understand pointer, remember learn C language learning is difficult to understand object-oriented C + +, it is hard to understand when you learn OC Block, while Swift as one kind of new language, is bound to have a lot of new things makes us difficult to understand, such as closure, tuples, optional type, functional programming, etc., the above is the entire content of this article, this article only to publish a 1 point view, closures, if have what shortage of place still hope correct, thank you.


Related articles: