- 创建基础闭包
let driving = {
print("创建基础闭包")
}
driving()
- 在闭包中接受参数
let driving2 = {(place: String) in
print("I'm going to \(place) in my car.")
}
- 闭包和函数的一个区别:调用闭包时不使用参数标签
driving2("London")
- 从闭包中返回值
let driving3 = {(place: String) in
return "I'm going to \(place) in my car."
}
print("Yesterday, " + driving3("New York"))
- 将闭包作为参数
func travel(action:() -> Void) {
print("I'm getting ready to go.")
action()
print("I arrived!")
}
travel(action: driving)
- 尾随闭包语法
travel {
print("I'm drving in car.")
}
- 当闭包接受参数时,将其用作参数
func travel2(action: (String) -> Void) {
print("I'm getting ready to go.")
action("ShangHai")
print("I arrived!")
}
- 使用结尾封闭语法调用函数travel2,闭包代码需要接收字符串
travel2{(place: String) in
print("I'm going to \(place) in my car.")
}
- 闭包返回值时,将其用作参数
func travel3(action: (String) -> String){
print("I'm getting ready to go.")
let description = action("ShangHai")
print(description)
print("I arrived!")
}
travel3{(place:String) -> String in
return "I'm going to \(place) in my car."
}
- 参数名称缩写
Swift
可以为闭包的参数提供自动名称,这些名称以【$
】命名,从0
开始计数
travel3{
//只有一行代码时可以省略【return】
"I want to go to \($0)"
}
- 具有多个参数的闭包
func myTravel(action: (String,Int) -> String){
print("I'm getting ready to go.")
let description = action("ShangHai",6)
print(description)
print("I arrived!")
}
myTravel{
"I had go to \($0) \($1) times."
}
- 从函数返回闭包
前一个【
->
】:指定函数的返回值(其实就是闭包)
后一个【->
】:制定闭包的返回值
func xTravel() -> (String) -> Void {
return {
print("今天学习了\($0)")
}
}
xTravel()("Swift")
- 捕获值
若在闭包内部使用了函数中创建的值,则会发生闭包捕获
func xTravel2() -> (String) -> Void {
var counter = 1
return {
print("今天学习了\($0) \(counter)小时")
counter += 1
}
}
let x2=xTravel2()
x2("SwiftUI")
x2("Swift")
x2("小黄书")
总结
- 闭包可以分配给变量,然后调用它们
- 闭包可以接受参数和返回值,例如常规函数
- 闭包可以作为参数传递给函数,并且这些闭包可以具有自己的参数和返回值
- 如果函数的最后一个参数是闭包,则可以使用尾随闭包语法
Swift
会自动提供诸如$0
和$1
之类的参数缩写名称,但并非每个人都使用这个写法- 如果在闭包内部使用外部值,则将捕获它们,以便闭包以后引用它们
本博文中的案例来自【100 Days of SwiftUI——闭包】
转载一个网友对闭包写法的总结
- 什么是闭包
闭包:就是自包含的代码块,可以在代码中被传递和使用。
闭包,实际上就是OC里面的blocks,在其它语言里面就是匿名函数。
闭包在swift中有几种形式,我们的主题是闭包的简写,关于闭包更多的概念,就不详细讲解了。
- 完整函数的写法
我们用数组的sorted(by:)排序方法来举例。该方法会返回一个与原数组大小相同,内部元素不同的数组,对原数组,该方法不会修改。sorted(by:)方法接收一个函数参数,函数的类型为:(Int, Int) -> Bool。
let arr = [1,2,3,8,6,5,4]
//定义出排序函数
func sortedMethod(_ s1: Int, _ s2: Int) -> Bool {
return s1 > s2
}
//排序
let new = arr.sorted(by: sortedMethod) //new的内容:8 6 5 4 3 2 1
- 使用闭包的写法
sorted(by:)方法,可以接收一个闭包用来代替函数作为参数
//使用闭包。in关键字,表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始
let new1 = arr.sorted(by: { (s1: Int, s2: Int) -> Bool in
return s1 > s2
}) //new的内容:8 6 5 4 3 2 1
- 根据上下文推断类型
因为闭包是作为sorted(by:)
方法的参数传入的,所以闭包的参数和返回类型可以被推断出来。所以闭包的参数和返回值声明可以被省略。如下:
//省略参数和返回值声明
let new2 = arr.sorted(by: { s1, s2 in
return s1 > s2
})
虽然说这样写很方便,但是会不利于阅读,完整的写完格式,能够提高代码的可读性。
- 单表达式闭包隐式返回
单行表达式的闭包可以省略return
关键字。
注意:必须是单行表达式的闭包才能省略return
关键字
//单行表达式省略return关键字
let new3 = arr.sorted(by: { s1, s2 in
s1 > s2
})
- 参数名称缩写
使用闭包来作为参数,swift
提供了参数名称缩写功能。就是说,可以使用$0
,$1
,$2
来依次使用参数,即使该参数没有被显式的定义。因此,可以省略掉in
关键字和前方的参数。
//参数名称缩写,$0和$1分别代表s1和s2
let new4 = arr.sorted(by: { $0 > $1 })
- 运算符方法
实际上,由于 > 该符号实际上是一个函数,类型为(Int, Int) -> Bool,所以,可以更加简单的写。
//运算符方法
let new5 = arr.sorted(by: > )
- 尾随闭包
如果需要一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强可读性。
注意点:
1.作为最后一个参数传递给函数。
2.使用尾随闭包可以不用写出其它参数标签
//尾随闭包
let new6 = arr.sorted() { $0 > $1 }
//如果闭包表达式是函数或者方法的唯一参数,可以省略()
let new7 = arr.sorted { $0 > $1 }
逃逸闭包(@escaping)
什么是逃逸闭包。
如果一个闭包被作为一个参数传递给一个函数,并且在函数return之后才被唤起执行,那么这个闭包是逃逸闭包。
并且这个闭包的参数是可以“逃出”这个函数体外的。
所有网络请求的函数,在完成调用请求后,直到响应返回,闭包才会被调用,所以这个类型的网络请求函数内等待响应的闭包就是逃逸闭包。这个类型的闭包,需要程序员手工加入一个@escaping
标记才可以编译通过。
如下代码,展示了一个非逃逸闭包,和一个逃逸闭包。后者已经被标记了@escapings
:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window : UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
func syncRequest(callBack: ()->Void ) {
callBack()
}
func asyncRequest( callBack: @escaping()->Void ) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
callBack()
})
}
syncRequest(){
print("callback")
}
asyncRequest(){
print("delay 1s callback")
}
window = UIWindow()
window!.rootViewController = UIViewController()
window!.rootViewController!.view.backgroundColor = .blue
window!.makeKeyAndVisible()
return true
}
}
函数DispatchQueue.main.asyncAfter
用来延时。此处延时1s
再调用callback
,演示了一个逃逸闭包的效果。
闭包可能需要引用当前上下文的变量,因此当调用者完成后,如果标记了逃逸闭包,那么当前调用的上下文依然会保持。如果在该标记的地方没有标记的话,会怎么样?不会在运行时报错,而是在编译期间就报错了。因为编译器知道你没有立即调用callback
。好智能。
swift
中闭包默认是不可逃逸的
关于创建默认不可逃逸闭包的好处: 最明显的好处就是编译器优化你的代码的性能和能力。如果编译器知道这个闭包是不可逃逸的,它可以关注内存管理的关键细节。
而且你可以在不可逃逸闭包里放心的使用self关键字,因为这个闭包总是在函数return
之前执行,你不需要去使用一个弱引用去引用self
.