类似 lambda 表达式.
建议: 跟着例子从上往下一步步跟着理解, 最后2小节难一点
闭包表达式 (Closure Expressions)
sorted 方法 (The Sorted Method)
根据方法对元素进行排序:
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
var reversedNames = names.sorted(by: backward)
print(reversedNames)
闭包表达式语法 (Closure Expression Syntax)
🕐 上述可以被改写为:
var reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
print(reversedNames)
即将匿名函数放在 {}
内, in
后接函数体
🕑 因为函数体就一个表达式, 可以被写在一行:
var reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
print(reversedNames)
从上下文中推断类型 (Inferring Type From Context)
因为可以从上下文中推断类型, 上述可以被改写为:
var reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
print(reversedNames)
单行表达式的隐式返回 (Implicit Returns from Single-Expression Closures)
单行表达式可省略 return:
var reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
print(reversedNames)
参数名简记法 (Shorthand Argument Names)
使用 $x 来快速获取参数:
var reversedNames = names.sorted(by: { $0 > $1 } )
print(reversedNames)
x 的最大值+1表示该闭包所接受的参数数量
运算符方法 (Operator Methods)
因为 String 实现了比较操作符方法, 即接受两个 String 参数并返回布尔值, 这刚好符合要求:
var reversedNames = names.sorted(by: > )
print(reversedNames)
尾闭包 (Trailing Closures)
调用闭包类型参数时, 可以将闭包写在后面:
func someFunc(closure: () -> Void) {
// function body goes here
}
🕐 常规调用:
someFunc(closure: {
// closure's body goes here
})
🕑 尾调用:
someFunc() {
// trailing closure's body goes here
}
示例 1
🕐 例如上面的例子可改写为:
var reversedNames = names.sorted() { $0 > $1 }
print(reversedNames)
🕑 如果闭包是唯一参数, 括号可以省略:
var reversedNames = names.sorted { $0 > $1 }
print(reversedNames)
示例 2
下面以数组的 map
方法为例子:
let d = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let a = [16, 58, 510]
let s = a.map { (n) -> String in
var n = n
var output = ""
repeat {
output = d[n % 10]! + output
n /= 10
} while n > 0
return output
}
示例 3
当有多个闭包参数时, 通过尾闭包传递第一个参数时名称可以省略, 后面的要标出:
func loadFile(from server: String, upload: (String) -> Void, onFailure: () -> Void) {
print("Downloaded file from \(server)")
if server.count <= 10 {
upload("Uploaded \(server.count) bytes.")
} else {
onFailure()
}
}
调用:
loadFile(from: "Youtube") { file in
print(file)
} onFailure: {
print("Couldn't upload the file.")
}
值获取 (Capturing Values)
闭包可以从上下文环境中获取值:
func getIncrease(step: Int) -> () -> Int {
var total = 0
func increaser() -> Int {
total += step
return total
}
return increaser
}
每调用一次 f()
返回的值都会增加:
let f = getIncrease(step: 10)
print(f())
print(f())
即闭包外的量与闭包绑定了
闭包为引用类型 (Closures Are Reference Types)
即值传递类型为指针传递
闭包逃逸 (Escaping Closures)
指的是被作为参数传递的闭包, 在函数返回后还可以被调用.
🕐 常见的方式是将其存储到外部变量中:
var outer: [() -> Void] = []
func toOuter(c: @escaping () -> Void) {
outer.append(c)
}
使用 @escaping
定义一个可逃逸的闭包
🕑 定义一个具有非逃逸的闭包的函数:
func noOuter(c: () -> Void) {
c()
}
🕒 定义一个类调用这两个函数:
class SomeClass {
var x = 10
func doThing() {
toOuter { self.x = 99 } // 等价为 { [self] in x = 99 }
noOuter { x = 200 }
}
}
对于可逃逸闭包, 需要显示指明要捕获 self, 或者将其添加到闭包的捕获列表中, 即上面的 [self]
🕓 调用该类的方法:
let myClass = SomeClass()
myClass.doThing()
print(myClass.x) // 200
🕔 调用该类的逃逸闭包:
outer.first?()
print(myClass.x) // 99
🕑 对于值类型则情况不同:
struct SomeStruct {
var x = 10
mutating func doSomething() {
noOuter { x = 200 } // Ok
toOuter { x = 100 } // Error
}
}
值类型的 self 是隐式传递的, 但是逃逸闭包不能捕获值类型的 self, 因而这里将报错.
值类型不允许共享可变性, 也就是多个代码部分对同一数据的读写权限. 值类型被传递时将会创建一个独立的副本, 那么你修改了一个其中一个副本并不会影响到另一个副本; 因而, 假设值类型可以逃逸, 那么在逃逸闭包中对其修改后是新创建一个副本还是修改原有副本? (值类型的定义已结束)
自动闭包 (Autoclosures)
自动闭包用于封装一个表达式, 其返回值为表达式的执行结果
🕐 延迟执行:
var a = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let c = { a.remove(at: 0) }
print(a.count) // 5
print("Remove \(c())")
print(a.count) // 4
自动闭包中的表达式只有被调用的时候才会执行
🕑 作为参数时的延迟执行:
func test(c: () -> String) {
print("Remove \(c())")
}
test(c: { a.remove(at: 0) })
print(a.count) // 3
🕒 可以在闭包参数前使用 @autoclosure
, 从而在传参时省略 {}:
func test(c: @autoclosure () -> String) {
print("Remove \(c())")
}
test(c: a.remove(at: 0))
🕒 可以和 @escaping
一起使用:
var outer: [() -> String] = []
func collect(_ c: @autoclosure @escaping () -> String) {
outer.append(c)
}
传递自动闭包:
collect(a.remove(at: 0))
collect(a.remove(at: 0))
print("Collected \(outer.count) closures.")
调用闭包:
for c in outer {
print("Now serving \(c())")
}