Swift5.1 闭包笔记
闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数(Lambdas)比较相似。
import UIKit
var str = "Hello, playground"
print(str)
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
// sorted(by:) 方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回 true,反之返回 false。
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversedNames = names.sorted(by: backward)
print(reversedNames)
/// 闭包表达式语法
///{ (parameters) -> return type in
/// statements
///}
///(String , String) -> Bool
/// 闭包表达式基本语法
reversedNames = names.sorted(by: { (s1: String , s2: String) -> Bool in return s1 > s2 })
print("内联闭包表达式\(reversedNames)")
/// 根据上下文推断类型
///实际上,通过内联闭包表达式构造的闭包作为参数传递给函数或方法时,总是能够推断出闭包的参数和返回值类型。这意味着闭包作为函数或者方法的参数时,你几乎不需要利用完整格式构造内联闭包
reversedNames = names.sorted(by: { s1 ,s2 in return s1 > s2 })
print("根据上下文推断类型\(reversedNames)")
/// 单表达式闭包和隐式返回 无return
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 })
/// 参数名称缩写 自动为内联闭包提供了参数名称缩写功能,你可以直接通过 $0,$1,$2
reversedNames = names.sorted(by: { $0 > $1 })
print("参数名缩写\(reversedNames)")
/// 运算符方法
/// 其作为一个函数接受两个 String 类型的参数并返回 Bool 类型的值。而这正好与 sorted(by:) 方法的参数需要的函数类型相符合。因此,你可以简单地传递一个大于号,Swift 可以自动推断找到系统自带的那个字符串函数的实现:
reversedNames = names.sorted(by: >)
/// 尾随闭包 (block)
// 尾随闭包是一个书写在函数圆括号之后的闭包表达式,函数支持将其作为最后一个参数调用。
func someFunctionThatTakesAClosure(closure: () -> Void) {
//函数内容
}
// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure(closure: {
//闭包主体部分
})
// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
//闭包主体部分
}
/// 使用尾随闭包的形式改写在 sorted(by:) 方法圆括号的外面:
reversedNames = names.sorted(){ $0 > $1 }
//如果闭包表达式是函数或方法的唯一参数,则当你使用尾随闭包时,你甚至可以把 () 省略掉:
reversedNames = names.sorted {$0 > $1}
/// 数组中使用尾随闭包
let digitNnames = [
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]
let strings = numbers.map { (number) -> String in
var number = number
var output = ""
repeat {
output = digitNnames[number % 10]! + output
number /= 10
} while number > 0
return output
}
print("尾随闭包\(strings)")
/// 值捕获
//嵌套函数
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
// print("makeIncrementer 外\(runningTotal)")
func incrementer() -> Int {
//捕获引用保证了 runningTotal 和 amount 变量在调用完 makeIncrementer 后不会消失,并且保证了在下一次执行 incrementer 函数时,runningTotal 依旧存在。
// print("incrementer 内 \(runningTotal)")
runningTotal += amount
return runningTotal
}
return incrementer
}
//为了优化,如果一个值不会被闭包改变,或者在闭包创建后不会改变,Swift 可能会改为捕获并保存一份对值的拷贝。
let incrementByTen = makeIncrementer(forIncrement: 10)
//调用方法多少只会
print(incrementByTen())
print(incrementByTen())
print(incrementByTen())
print(incrementByTen())
print(incrementByTen())
let incrementBySeven = makeIncrementer(forIncrement: 7)
print(incrementBySeven())
print(incrementByTen())
print(incrementBySeven())
/// 逃逸闭包 (@escaping 最后执行)
//当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注 @escaping,用来指明这个闭包是允许“逃逸”出这个函数的
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
print("逃逸闭包")
completionHandlers.append(completionHandler)
}
func someFunctionWihtNoescapingClosure(closure: () -> Void) {
print("闭包")
closure()
}
/// 将一个闭包标记为 @escaping 意味着你必须在闭包中显式地引用 self
class SomeClass {
var x = 10
func doSomething() {
print("进入 doSomething")
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWihtNoescapingClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething()
print("隐式\(instance.x)")
completionHandlers.first?()
print("显示\(instance.x)")
/// 自动闭包 (@autoclosure 只有调用的时候才执行)
//自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包。
//自动闭包让你能够延迟求值,因为直到你调用这个闭包,代码段才会被执行
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
let customerProvider = { customersInLine.remove(at: 0) } //返回值 () -> String,一个没有参数且返回值为 String 的函数
print(customersInLine.count)
print("Now serving \(customerProvider())!")
print(customersInLine.count)
//将闭包作为参数传递给函数时,你能获得同样的延时求值行为
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
/// 如果你想让一个自动闭包可以“逃逸”,则应该同时使用 @autoclosure 和 @escaping 属性。
// customersInLine i= ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
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!”
//在上面的代码中,collectCustomerProviders(_:) 函数并没有调用传入的 customerProvider 闭包,而是将闭包追加到了 customerProviders 数组中。这个数组定义在函数作用域范围外,这意味着数组内的闭包能够在函数返回之后被调用。因此,customerProvider 参数必须允许“逃逸”出函数作用域。