点赞评论,感觉有用的朋友可以关注笔者公众号 iOS 成长指北,持续更新
本文是 《Swift 100 Days》系列的的第 6 天, Swift 100 Days 是笔者记录自己 Swift 学习的记录,欢迎各位指正。
在第五天 函数 学习中,我们学会使用 func 关键字创建了一个函数。然而,Swift 中还有另一种特殊类型的函数,称为 闭包,它可以在不使用关键字 func 和函数名的情况下进行定义。
与函数一样,闭包可以接受参数和返回值。它还包含一组语句,这些语句在您调用它之后执行,并且可以作为函数分配给变量/常量。
闭包作为 Swift 中的一个复杂概念。所以我们花费两天时间来进行闭包的学习。
闭包
Closures 在 Swift 官方文档上是这么介绍的:
Closures are self-contained blocks of functionality that can be passed around and used in your code.
闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数(Lambdas)比较相似。
通俗点来说换,闭包就是一个可以分配给变量的代码块。然后可以在代码中传递它,例如传递给另一个函数。然后该函数调用闭包并执行其代码,就好像闭包是一个普通函数一样。尽管它们的工作方式类似于函数,但它们的编写方式略有不同。
在 Objective-C 我们可以使用如下方法来创建一个对象。
NSString *string = ({ @"你好"; });这是一个GNU(非标准)C语言扩展,称为语句表达式。gcc、clang和其他一些编译器支持该语法。
声明闭包
闭包表达式语法一般类似于以下形式:
{ (parameters) -> return type in
statements
}
注意返回类型之后使用了 in 关键字。in 关键字用于分隔闭包中的语句。闭包接受参数并可以返回值。
创建一个最基本的闭包
如同函数学习一样,我们来学习创建一个无参数无返回值的最基本的闭包。
let learnSwift = {
print("Closures are like functions")
}
learnSwift()
let learnSwift1 = { () -> Void in
print("Closures are like functions")
}
learnSwift1()
let learnSwift2 = { () -> () in
print("Closures are like functions")
}
learnSwift2()
上面的三个是等价的,我们可以在 Playground 中看到上面三个闭包的类型都是 () -> ()。正如我们所知,Swift 可以进行类型推断,那么我们可以显示表明对应变量/常量的类型
let learnSwift3: () -> () = {
print("Closures are like functions")
}
闭包类型我们会在尾随闭包的时候进行详细介绍。
注意:
我理解在
{}里实现的功能其实是一个初始化的过程,所以在使用闭包时,一定要一个接收值。并不能定义一个只初始化的功能。就像我们不能在代码的一行中写下1
闭包参数
如同函数一样,闭包可以接收参数。我们可以按照下面的方法去定义一个接受字符串参数的闭包。
let sayHello = { (name: String) in
print("Hello, \(name)")
}
sayHello("WWH")
let sayHello1: (String) -> () = { name in
print("Hello, \(name)")
}
sayHello1("YHG")
let sayHello2: (String) -> () = {
print("Hello, \($0)")
}
sayHello2("SY")
Swift 用 关键字 in 来分隔闭包参数和返回值或语句。这里我们需要注意以下几点
- 如果我们使用类型推断来定义一个闭包的话, 我们必须要设置参数名和参数类型
- 如果我们声明时确定了参数类型, 在
{ parameter in }中,我们可以不使用(parameter name:parameter type)进行定义 - 如果我们声明时确定了参数类型,我们可以的
{}中省略参数名,并用用$0表示 - 如果我们声明时确定了参数类型, 我们可以的
{}中省略参数名,可以用_来代替
参数标签
与函数不同的是,在调用闭包时,不包括它的任何参数的名称,闭包是没有参数标签的。闭包所定义的参数名称其实主要是为了内部使用的。
传入多个参数
和函数一样,我们可以为闭包传入多个参数。
let sayHelloMultiple = { (name: String, age: Int, score: Double) in
print("\(name) is \(age) year old ,score is \(score)")
}
let sayHelloMultiple1: (String,Int,Double) -> Void = { name,age,score in
print("\(name) is \(age) year old ,score is \(score)")
}
let sayHelloMultiple2: (String,Int,Double) -> () = {
print("\($0) is \($1) year old ,score is \($2)")
}
sayHelloMultiple("WWH",35,87.0)
sayHelloMultiple1("WWH",35,87.0)
sayHelloMultiple2("WWH",35,87.0)
具有可变参数的闭包
let sayHelloMore = { (name: String...) in
print("Hello, \(name)")
}
sayHelloMore("XM","PED")
let sayHelloMore1:(String...) -> () = { (name: String...) in
print("Hello, \(name)")
}
sayHelloMore1("WD","HDM", "XWQ")
与之前不一样的话,哪怕设置了函数类型,在{}也要定义这个参数,不然会报类型错误。
闭包返回值
参照表达式,我们可以在 -> 和 in 关键字之间定义函数的返回值
let drivingWithReturn = { (place: String) -> String in
return "I'm going to \(place) in my car"
}
let drivingWithReturn1:(String) -> String = {
return "I'm going to \($0) in my car"
}
let drivingWithReturn2:(String) -> String = {
"I'm going to \($0) in my car"
}
同函数一样,闭包的返回值可以是任意类型的。String、Int 甚至是另一个函数。
多重返回值闭包
你同样可以用元组(tuple)类型让多个值作为一个复合值从闭包中返回。
let multipleReturn = { () -> (String, Int) in
return ("HCP", 29)
}
let user0 = multipleReturn()
print("\(user0.0),\(user0.1)")
let multipleReturn1 = { () -> (name: String, age: Int) in
return ("HL", 19)
}
let user = multipleReturn1()
print("\(user.name),\(user.age)")
闭包的隐式返回
在 Swift 闭包处理的时候,如果闭包没有返回值,在{} 里面定义的时候,你可以隐藏 -> ()。但是注意你不可以隐藏 ()
当存在单个返回值,但是在 in 关键字后面只有一个语句时,不仅可以隐藏 -> () 也可以省略 return 关键字
let drivingWithReturn3 = { (place: String) in
return "I'm going to \(place) in my car"
}
let drivingWithReturn4 = { (place: String) in
"I'm going to \(place) in my car"
}
drivingWithReturn3("SuZhou")
drivingWithReturn4("SuZhou")
单个返回值的多个返回值,或者说有多个语句的时候,还是需要定义返回值类型的。
将闭包作为函数参数传递
我们可以将闭包作为函数参数在定义函数时使用。我们然后我们在函数调用时可以使用闭包。
func play(using playType: () -> Void) {
print("Let's play a game")
playType()
}
play(using: {
print("Fetch!")
})
func deliverTalk(name: String, type: () -> Void) {
print("My talk is called \(name)")
type()
}
deliverTalk(name: "My Awesome Talk", type: {
print("Here's a great talk!")
})
尾随闭包
如果函数的最后一个参数是闭包,Swift 允许您使用特殊的语法,称为尾随闭包语法。不是将闭包作为参数传递,而是将它直接传递到大括号内的函数之后。尾随闭包是一种特殊的将闭包作为函数参数使用。
唯一即最后
上面的两个例子我们同样可以这样子来用
play {
print("Fetch! Trailing closure")
}
deliverTalk(name: "My Awesome Talk") {
print("Here's a great talk! Trailing closure")
}
尾随闭包作为一种常见的 Swift 语法。需要我们好好掌握。
对比上面对于最后一个参数是闭包的函数的调用示例。我们可以看到使用尾随闭包可以让代码更加简短,可读性更好。所以出于可读性的原因,建议使用。
Xcode 中调用定义了最后一个参数是尾随闭包的参数时,会默认使用尾随闭包的样式。
在尾随闭包中传递参数
尾随闭包语法作为一种特殊的闭包用法,同样可以传递参数。
func makePizza(addToppings: (Int) -> Void) {
print("The dough is ready.")
print("The base is flat.")
addToppings(3)
}
makePizza { (toppingCount: Int) in
let toppings = ["ham", "salami", "onions", "peppers"]
for i in 0..<toppingCount {
let topping = toppings[i]
print("I'm adding \(topping)")
}
}
makePizza {
let toppings = ["ham", "salami", "onions", "peppers"]
for i in 0..<$0 {
let topping = toppings[i]
print("I'm adding \(topping)")
}
}
同闭包或函数中使用参数一致,千万不要修改参数类型。
在尾随闭包中返回值
尾随闭包自然同样支持返回值了。
func playSong(_ name: String, notes: () -> String) {
print("I'm going to play \(name).")
let playedNotes = notes()
print(playedNotes)
}
playSong("Mary Had a Little Lamb") {
return "EDCDEEEDDDEGG"
}
尾随闭包支持返回值,闭包有一个疯狂的用法
let squared = { $0 * $0 }(12)
可不可以这样使用? 如果可以的话 squared 是什么类型?
感谢你阅读本文! 🚀