swift基础——闭包

一. 闭包简介

  • 一个函数和它所捕获的变量/常量环境组合起来,成为闭包
  • 如果闭包捕获了局部变量,那么会将该局部变量保存到堆空间
  • 闭包和类非常相似,捕获的局部变量在堆空间,前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?
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容