Swift闭包(Closure)

闭包表达式(Closure Expression)

{
	(参数列表) -> 返回值类型 in
	函数体代码
}
复制代码

Swift函数示例:

func sum(_ v1:Int, _ v2: Int) -> Int {
	return v1 + v2
}
// 函数调用
let c = sum(10, 20)
复制代码

Swift闭包表达式示例:

// 闭包表达式
var fn = {
	(v1:Int,v2:Int) -> Int in
	return v1 + v2
}

let s = fn(20,30)
复制代码

闭包表达式的简写:

// 函数
func exech(v1:Int,v2:Int,fn:(Int,Int) ->Int) {
	print(fn(v1,v2))
}

exech(v1: 10, v2: 20) { (v1:Int, v2:Int) -> Int in
	return v1 + v2
}

exech(v1: 20, v2: 30) { v1,v2 in
     v1 + v2
}

exech(v1: 40, v2: 50) {
	$0 + $1
}

exech(v1: 50, v2: 60,fn: +)

// 忽略参数
exech(v1: 10, v2: 20) { (_, _)  in
	500
}
复制代码

输出结果为:

30
50
30
50
70
90
110
500
复制代码

闭包(Closure)

  • 网上有两种关于闭包的定义:
  • 比较严谨的定义是:一个函数和它所捕获的变量\常量环境组合起来,称为闭包
  1. 一般定义在函数内部的函数
  2. 一般它捕获的是外层函数的局部变量\常量
typealias Fn = (Int) -> Int
func getFn() -> Fn {
	var num = 0
	func plus(_ i:Int) -> Int {
		num += i
		return num
	}
	return plus
}

var fn1 = getFn()
var fn2 = getFn()

print(fn1(1)) // 1
print(fn1(2)) // 3
print(fn1(3)) // 6
print(fn1(4)) // 10
print(fn2(5)) // 5
print(fn2(6)) // 11
print(fn2(7)) // 18
复制代码
  • 还有一种理解是:可以把闭包想象成是一个类的实例对象
  1. 内存在堆空间
  2. 捕获的局部变量\常量是对象的成员(属性)
  3. 组成闭包的函数是类内部定义的方法
class Closure {
	var num = 0
	func plus(_ i: Int) -> Int {
		num += i
		return num
	}
}

var cs1 = Closure()
var cs2 = Closure()
print(cs1.plus(1)) // 1
print(cs1.plus(2)) // 3
print(cs1.plus(3)) // 6
print(cs1.plus(4)) // 10
print(cs2.plus(5)) // 5
print(cs2.plus(6)) // 11
print(cs2.plus(7)) // 18
复制代码

注意⚠️:闭包真正捕获变量是在函数返回调用的时候:

typealias Fn = (Int) -> Int
func getFn() -> Fn {
	var num = 0
	func plus(_ i:Int) -> Int {
		num += i
		return num
	}
	// 修改num值
	num = 10
	return plus
}
复制代码

这时捕获的num初始值就是10而不是0相应的输出结果就会加10

自动闭包

  1. 函数的参数都为Int数据类型
func getFirstPositive(_ v1:Int, _ v2:Int) -> Int {
	return v1 > 0 ? v1 : v2
}

print(getFirstPositive(10,20)) // 10
print(getFirstPositive(-1, 2)) // 2
print(getFirstPositive(0, -5)) // -5
复制代码
  1. 函数的参数一个为Int类型,另一个是闭包表达式
func getFirstPositive(_ v1:Int, _ v2:()-> Int) -> Int {
	return v1 > 0 ? v1 : v2()
}

print(getFirstPositive(10,{20}))
//print(getFirstPositive(-1, 2)) // 会报错
复制代码
  1. 自动闭包作为参数
func getFirstPositive(_ v1:Int, _ v2:@autoclosure () -> Int) -> Int {
	return v1 > 0 ? v1 : v2()
}

 // print(getFirstPositive(10,{20}))会报错
print(getFirstPositive(-1, 2)) // 2

func getNum() -> Int {
	return 10 + 20
}
print(getFirstPositive(-2, getNum()))  // 30
复制代码
  • 为了避免与期望冲突,使用@autoclosure的地方最好注释清除:这个值会被推迟执行
  • @autoclosure会自动将参数2封装成闭包{2}
  • @autoclosure只支持() -> T格式的参数
  • @autoclosure并非只支持最后一个参数
  • 空合并运算符 ?? 使用了@autoclosure技术,延迟执行增加效率
  • 有@autoclosure和无@autoclosure可以构成函数重载

逃逸闭包

  • 当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为函数的参数需要标明@escaping,用来指明这个闭包是允许“逃逸”出这个函数。
  • 一种能使闭包“逃逸”的方法就是在函数的实现中将闭包存储在函数外的一个变量,在特定的情况下才调用。这种情况下,闭包需要“逃逸”出函数,因为闭包需要在函数返回之后被调用。例如:
var comples:[()->Void] = []
// 包含逃逸闭包的函数
func someFuncWiEscaping(compleHandler:@escaping ()->Void) {
	comples.append(compleHandler)
}
复制代码

someFuncWiEscaping(:)函数中的闭包被外部的数组所保存。

  • 将一个闭包标记为@escaping意味着你必须在闭包中显式地引用self才能使用当前类中的属性或者方法调用。
  • 非逃逸闭包可以隐式引用self。
func someFunWithNoneEscaping(closure:() -> Void) {
	closure()
}

class Student {
	var x = 10
	func doSomething() {
	// 如果不显式调用self会编译报错
		someFuncWiEscaping { self.x = 100 }
		someFunWithNoneEscaping { x = 200 }
	}
}

let stu = Student()
stu.doSomething()
print(stu.x)
// 200
comples.first?()
print(stu.x)
// 100
复制代码
分类:
iOS
标签: