//闭包closures 类似于 OC的 block, 闭包可以捕获(capture)并且存储代码块中指向常量或变量的指针
//Global and nested functions 都是闭包的特例, 闭包有三种形式:
//(1)Global functions 是有名字但是没有 capture 任何值的闭包
//(2)Nested functions 是有名字并且 capture 封闭函数中值的闭包
//(3)闭包语句是没有名字的,轻量级的语法(syntax), 可以 capture 周围 context中的常量和变量指针
//一. 闭包的基本形式
/**
闭包的表达形式
{ (parameters) -> return type in
statements
}
内联闭包(inline closure)
闭包语句体由关键字 in 引出, in 表明参数和返回值定义完成, 闭包语句体开始
可以为 inout 参数, 但是不能有默认值.可变数量参数也可以使用.元组可以作为参数和返回值
*/
//用闭包实现 sorted(by: (String, String) throws -> Bool) 方法,参数 by 是一个闭包
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
var reversedNames: [String] = []
//调用sorted(by: (String, String) throws -> Bool),参数 by 是一个闭包
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
//闭包优化(optimizations)
//(1)从 context 中推断参数和返回值类型
//上述 sorted 例子中,因为 sorted(by:) 方法是由字符串数组调用,所以它的参数一定是 (String, String) -> Bool 函数类型的, 因此可以忽略不写简写为以下形式
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 })
//inline closure 作为函数或者方法的参数时, 总是可以推断参数和返回值类型, 因此这种情况下都可以简写
//(2) Single-Expression Closures, 单语句闭包的隐式返回
//必须是单语句闭包, 且返回值类型明确
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 })
//(3) 简写参数名
//对于 inline closures, Swift 提供简写参数名, $1, $2, ....
//如果在闭包语句中使用简写参数名,可以忽略闭包的参数列表.参数类型和数量会从函数类型推断, 关键字 in 也可以省略
reversedNames = names.sorted(by: { $0 > $1 })
//(4) 运算符方法
//Swift 的 String 类型定义了 string 特定的 > 方法的实现, 比较两个 String, 返回 Bool 值.类似这种特定的运算符方法可以直接使用
reversedNames = names.sorted(by: >)
//二. 尾随闭包(trailing closure)
//如果用闭包作为函数参数,并且闭包语句较长,可以使用 trailing closure.
//调用时,写在函数括号外,不用写 argument label
//函数声明
func someFunctionThatTakesAClosure(closure: () -> Void) {
//函数实现
}
//函数调用: 使用 trailing closures
someFunctionThatTakesAClosure() {
//闭包实现
}
//函数调用: 不使用 trailing closures
someFunctionThatTakesAClosure(closure: {
//闭包实现
})
//例1.
//上述 sorted(by:) 方法用 trailing closures 可以写为
reversedNames = names.sorted() { $0 > $1 }
//如果闭包语句是唯一的参数, 那么调用时函数后的括号()可以省略
reversedNames = names.sorted { $0 > $1 }
//例2.
//以下例子将数组中的数字变为英文形式
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
//参数 number 的类型不必说明,因为类型会根据映射(map)的数组中的值推断
//指定返回值类型为 String, 来表明输出数组中存储的数据类型
let strings = numbers.map() { (number) -> String in
//用参数给变量赋值,因为函数和闭包中的参数都是常量
var number = number
var output = ""
repeat {
//使用 !, 因为字典下标访问返回的是可选类型, 此处可以确定不为空所以强制解包
output = digitNames[number % 10]! + output
number /= 10
} while number > 0
return output
}
// strings 被推断为 [String] 类型
//输出 ["OneSix", "FiveEight", "FiveOneZero"]
//三. 闭包特性
//1. 捕获值 Capturing Values
//闭包可以 capture 周围 context 的常量和变量的指针, 访问并修改这个值
//capture 的是 value 的指针, 因此在函数调用后不会消失, 下次调用时还可以使用
//Swift 中闭包 capture 值的最简单例子是 nested function.一个 nested function 可以 capture 外部函数的参数,以及外部函数中定义的常量和变量
//函数
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
//调用
//capture 的是 value 的指针, 因此在函数调用后不会消失, 下次调用时还可以使用
let incrementByTen = makeIncrementer(forIncrement: 10)
incrementByTen()
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30
//再定义一个incrementBySeven, 它将会有自己新的独立的指针指向 runningTotal
let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// returns a value of 7
//incrementByTen 捕获的变量是独立的,不受影响
incrementByTen()
// returns a value of 40
//如果把一个类的一个属性值赋给闭包, 闭包又 capture 这个实例变量,会造成闭包和实例变量的strong reference cycle. Swift 使用 capture lists 来打破循环引用
//2. 闭包是指针类型
//上述例子中, incrementBySeven 和 incrementByTen是常量,但是这些闭包仍然可以改变它所捕获的runningTotal的值,这是因为 functions 和 closures 是指针类型
//incrementByTen 是常量,并非闭包本身是常量
let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// returns a value of 50
//3. Escaping Closures
//闭包作为参数, 但是在函数执行结束后调用.(不在主线程中立即执行的)
class SomeClass {
var x = 10
//在参数类型前加 @escaping 表明这个闭包允许 escape
//Escaping Closures 的一种实现方式是存储在一个函数外部定义的变量中, 使用外部变量存储时必须加 @escaping, 否则会编译报错
var completionHandlersArray: [() -> Void] = []
//escape 的闭包
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlersArray.append(completionHandler)
}
//没有 escape 的闭包
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
//没有逃逸的闭包, 没有逃逸的闭包不能写 argumentLabel
func nonEscapingClosure(closure: (_ param1: Int) -> Int) -> Void {
closure(1)
}
func doSomething() {
//将一个闭包声明为 @escaping, 意味着在闭包内部必须明确的指向 self
//没有声明为 @escaping, 意味着可以隐性的指向 self
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
//调用函数
let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints "200"
//没有逃逸的闭包
instance.nonEscapingClosure { (param1) -> Int in
return param1 + 1
}
//调用completionHandler
instance.completionHandlersArray.first?()
print(instance.x)
// Prints "100"
//4. 自动闭包
//自动闭包能动态的封装一个表达式为一个函数的参数,自动闭包不能带任何的参数,返回值是内部语句的返回值
//通常会调用有自动闭包的函数,而不是实现它,不能过度使用
//自动闭包让你可以delay evaluation, 因为闭包内部的代码在调用时才执行.
//delay evaluation通常用于有副作用(如耗时)或者需要大量计算的代码
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"
//customerProvider 是 () -> String 类型的
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// Prints "5"
//闭包调用时,才执行 remove 操作
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"
//(1) @autoclosure
//显式闭包(explicit closure)
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
//在以上显式闭包类型前加 @autoclosure
func serve1(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve1(customer: customersInLine.remove(at: 0)) //不用写大括号 {}
//(2) @autoclosure 和 @escaping 同时使用
//在闭包类型前加 @autoclosure @escaping
var customerProviders: [() -> String] = []
//因为将customerProvider 添加到了外部的数组中,所以要将 @escaping
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))
print("Collected \(customerProviders.count) closures.")
//"Collected 2 closures."
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}
//"Now serving Barry!"
//"Now serving Daniella!"