一. 闭包简介
- 一个函数和它所捕获的变量/常量环境组合起来,成为闭包
- 如果闭包捕获了局部变量,那么会将该局部变量保存到堆空间
- 闭包和类非常相似,捕获的局部变量在堆空间,前16个字节储存基本信息
二. 测试一下
普通闭包
//正常函数
func funcAdd (_ a: Int, _ b: Int) -> Int{
a + b
}
//闭包表达式
var closure = {
(a : Int, b : Int) -> Int in
return a + b
}
//匿名闭包
//{
// (a : Int, b : Int) -> Int in
// return a + b
//}(10, 20)
funcAdd(1, 5)
closure(1, 5)
尾随闭包
//尾随闭包是一个被书写在函数调用括号外面的闭包表达式,使用尾随闭包增强函数的可读性
func exe(a: Int, b: Int, closure:(Int, Int)->Int) {
print(closure(a, b))
}
exe(a:100, b:1, closure:{a, b -> Int in return a + b}) //原始
exe(a:100, b:1, closure:{a, b -> Int in a + b}) //简化,去掉return
exe(a:100, b:1, closure:{$0 + $1}) //简化,去掉a,b
exe(a:100, b:1, closure:+) //简化,直接用+
//使用尾随闭包, 就是将最后一个闭包在参数项后面展开
exe(a:100, b:1) {
$0+$1
}
//如果闭包是函数的唯一实参,而且使用了尾随闭包的方式,那么可以简写
func exeSimple(closure:(Int, Int)->Int) {
print(closure(1, 2))
}
exeSimple(closure: {a, b -> Int in return a + b})
exeSimple(){
$0 + $1
}
exeSimple{$0 + $1}
//测试demo
var arr = [1, 3, 8, 2, 7, 6]
print(arr.sorted())
//print(arr.sorted(by: <#T##(Int, Int) throws -> Bool#>))
print(arr.sorted{$0 > $1})
闭包对参数的引用
//----------------------------- 1-------------------------------
func retClosure() -> (Int)->Int {
var num = 100
func innerClo(_ a: Int) -> Int {
num += a
return num
}
return innerClo
}
retClosure()(1) //101
retClosure()(2) //102
retClosure()(3) //103
//num并没有累加
//----------------------------- 2-------------------------------
//换一种调用方式,num累加了,说明它捕获了num,并且一直再使用这个num
typealias fn = (Int)->Int
var fff = retClosure()
fff(1) //101
fff(2) //103
fff(3) //106
//----------------------------- 3-------------------------------
var num = 100
func retClosureOut() -> (Int)->Int {
func innerClo(_ a: Int) -> Int {
num += a
return num
}
return innerClo
}
retClosureOut()(1) //101
retClosureOut()(2) //103
retClosureOut()(3) //106
//把num放到了外面,num累加
//----------------------------- 4-------------------------------
func retClosureChangeNum() -> (Int)->Int {
var num = 100
func innerClo(_ a: Int) -> Int {
num += a
return num
}
num = 200
return innerClo
}
retClosureChangeNum()(1) //201 很神奇,闭包捕获变量是在return 之前. 所以第一个100被200替换了
//----------------------------- 5-------------------------------
typealias retfn = (Int)->Int
func moreClosure() ->(retfn, retfn) {
var num1 = 0
func add(_ a: Int) -> (Int) {
num1 += a
return num1
}
func sub(_ a: Int) -> (Int) {
num1 -= a
return num1
}
return (add, sub)
}
let (add, sub) = moreClosure()
add(10) //10
sub(3) //7
add(10000) //10007
// 当两个闭包同时引用一个局部变量时,不会创建两个堆空间来保存该变量,而是共用一个变量
// 闭包捕获的局部变量在堆空间,前16个字节储存基本信息
自动闭包
//: # 自动闭包
//: * @autoclosure 主要是用来优化性能的,避免多余的计算之类,有可能会延迟调用
//: * @autoclosure只支持无参的闭包表达式,而且必须有返回值,局限性比较大
//: * @autoclosure支持重载
总的来说:自动闭包就是把普通值包装成闭包,可以在函数内部决定闭包调用时机(鸡肋🐔)
//@autoclosure
func getFirstNum1(_ a: Int, _ b: Int) -> Int {
a > 0 ? a : b
}
func getFirstNum2(_ a: Int, _ b: ()->Int) {
a > 0 ? a : b()
print(a)
}
getFirstNum2(10,{() -> Int in return 20})//10大于0了,期望不执行后面的闭包,所以就有了可以延迟调用的闭包(自动闭包)
getFirstNum2(10,{20})
getFirstNum2(10){20}
//加上@autoclosure自动闭包,可以不写{}了 -> 编译器特性,自动加了{}
func getFirstNum3(_ a: Int, _ b: @autoclosure ()->Int) {
a > 0 ? a : b()
print(a)
}
getFirstNum3(10,20)
let getNumFor4 = {()->Int in
let a = 100
let b = 200
print("这行会走吗?")
return a + b
}
func getFirstNum4(_ a: Int, _ b: @autoclosure ()->Int) {
a > 0 ? a : b()
print(a)
}
getFirstNum4(10, getNumFor4()) //加了@autoclosure后,编译器发现10 > 0, 所以就不会调用getNumFor4这个闭包表达式,节省了性能(延迟执行)
getFirstNum4(-10, getNumFor4()) //会打印
// ?? 就是基于自动闭包实现的, 下面就是他的实现
//public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?) rethrows -> T?
网友评论