关键词:Swift, iOS, 闭包, Block
概述
- 代码块,可以在代码中被传递和使用。
- 函数和闭包都是引用类型。
-
将函数或闭包赋值给一个常量或变量,实际上都是将常量或变量的值设置为函数或闭包的引用。
- 函数是一种特殊的闭包
- 全局函数是一个有名字但不会捕获任何值的闭包
- 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
- 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包
- 参数可以是inout参数,但不能有默认值
示例
闭包类型的定义
typealias myBibaoType = (Int, String) -> Void
闭包的定义
//闭包的定义。没有实现的话,要定义成可选值
var myBibao2:((Int, String) -> String)?
//定义的同时赋值
var myBibao:((Int, String) -> String) = {
(param1:Int, param2:String) -> String in
print("这在闭包里面:\(param1) \(param2)")
return "\(param1)" + param2
}
//赋值
myBibao2 = {
(param1:Int, param2:String) -> String in
print("这是在闭包2里面:\(param1) \(param2)")
return "\(param1)" + param2
}
//调用。闭包的解包跟可选值的解包有点不一样。也可以用可选绑定来处理
let resB2 = myBibao2!(111, "ddd")
闭包的形式
{ (parameters) -> return type in
statements
}
作用域
- 闭包中的代码,能够访问闭包作用域中的变量和函数。
- 使用in分离 参数和返回值的声明 与 闭包函数体。
let numbers = [11, 22, 33]
let num = numbers.map({
//in 前面的是闭包的参数及返回值声明
(number: Int) -> Int in
//in 后面的是闭包的函数体
let result = 3 * number
return result
})
print("获取到的结果:\(num)")
//获取到的结果:[33, 66, 99]
- swift能够根据上下文自动推断出参数和返回值的类型。
// (s1: String, s2: String) -> Bool 可以把参数类型和返回值类型,包括->都省略。直接使用 s1, s2 参数名称就行了。
let sortNumbers = numbers.sorted(by: { s1, s2 in
return s1 > s2
})
print("排序后的结果:\(sortNumbers)")
//排序后的结果:[33, 22, 11]
单行表达式
简单的闭包可以写成单行表达式。
let sortNumbers2 = numbers.sorted(by: { s1, s2 in return s1 > s2 })
print("单行表达式的结果:\(sortNumbers2)")
//单行表达式的结果:[33, 22, 11]
隐式返回
单行表达式闭包的隐式返回。单行表达式闭包可以省略return关键字。
let sortNums = numbers.sorted(by: { s1, s2 in s1 > s2 })
print("隐式返回的结果:\(sortNums)")
//隐式返回的结果:[33, 22, 11]
尾随闭包
- 闭包作为函数的最后一个参数,可以写在()后面。
- 闭包作为函数的唯一参数时,可以省略()。
//闭包作为参数的最后一个函数
func showSomeStr(param1:() -> Void) {
print("这在函数里面")
//闭包的调用
param1()
}
//普通的调用方法,不使用尾随闭包的函数调用方法
showSomeStr(param1: {
print("这是在闭包里面")
})
//使用尾随闭包的函数调用方法
showSomeStr() {
print("这是在闭包里面")
}
//闭包表达式是函数的唯一参数时,可以省略()
showSomeStr {
print("这是在闭包里面")
}
值捕获
类似block引用外部的对象。有点类似强引用。
如果将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,你将在闭包和该实例间创建一个循环强引用。(闭包持有这个实例,实例也持有这个闭包)
swift会负责被捕获变量的所有内存管理工作,包括释放不再需要的变量。
- 闭包可以在其被定义的上下文中捕获变量或常量。
- 即使定义这些变量或常量的作用域已经不存在。闭包仍然可以在闭包函数体内引用和修改这些值。
- 最常见的场景,函数嵌套。(内层函数可以调用外层函数的所用参数及定义的变量或常量)
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
//同:func makeIncrementer(forIncrement amount: Int) -> (() -> Int) {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
逃逸闭包
- 当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行。
- 例如:将闭包保存在一个函数外部定义的变量中。
- 逃逸闭包在闭包类型前加@escaping进行标识。
- 逃逸闭包内引用对象属性时,必须显式引用self.
//逃逸闭包,在前面加@escaping标记
var val1:((Int) -> String)? = nil
func ffun1 (param1:Int, param2:@escaping (Int) -> String) {
//将闭包保存在函数外的变量里(逃逸)
val1 = param2
print("这是在函数里面:\(param1)")
}
//函数调用
ffun1(param1: 11) { (xxParam:Int) -> String in
print("这是在闭包里:\(xxParam)")
return "\(xxParam*2)"
}
//闭包调用
let xxVal = val1!(33)
print("闭包的返回值:\(xxVal)")
自动闭包
- 经常使用,但是很少自己去实现。
- 没有参数。
- 返回{}内表达式的值。
- 只有调用这个闭包时,{}内的代码才会被执行。
- 在闭包类型前要加 @autoclosure进行标识。
使用自动闭包:
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// 打印出“5”
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// 打印出“5”
let res22 = customerProvider()
print("闭包返回的:\(res22)")
print(customersInLine.count)
// 打印出“4”
实现自动闭包:
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
// 打印“Now serving Ewa!”
让一个自动闭包逃逸:
同时使用 @autoclosure @escaping
闭包的简写过程
//完整
reversedNames = names.sorted(by: {
(s1: String, s2: String) -> Bool in
return s1 > s2
})
//或者
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
//简写,单行表达式
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
//再简写,省略参数类型
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
//再简写,隐式返回
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
//再简写,参数名缩写
reversedNames = names.sorted(by: { $0 > $1 } )
//再简写,运算符方法
reversedNames = names.sorted(by: >)
PREVIOUS制作一个快速关机的小工具
NEXTSwift泛型