美文网首页
Swift 闭包

Swift 闭包

作者: H丶ym | 来源:发表于2021-01-12 14:03 被阅读0次

什么是闭包

闭包是一个捕获了上下文常量或者变量的函数

闭包表达式

OC中的block类似,这个表达式需要具备

  • 作用域(也就是大括号)
  • 参数和返回值
  • 函数体(也就是in后面的代码)
var closure : (Int) -> Int = { (age: Int) in
    return age
}

这个闭包的参数为Int,返回值为Int
我们可以将这个闭包声明成一个可选类型

var clourse:((Int)->(Int))?

同时也可以作为函数的参数

func test(param:(Int)->(Int)){
    let a = param(10)
    print(a)
}

test { (param) -> (Int) in
    return param+1
}
尾随闭包

一种书写方式,用来提高代码可读性,当闭包表达式作为函数的最后一个参数时,将当前闭包表达式的{}放在函数外面,一般编译器会帮我们做

func test(_ a: Int, _ b: Int, _ c: Int, by: (_ item1: Int, _ item2: Int, _ item3: Int) -> Bool) -> Bool{
   return  by(a, b, c)
}

test(10, 20, 30){(_ item1: Int, _ item2: Int, _ item3: Int) -> Bool in
   return (item1 + item2 < item3)
}
闭包强大的推算能力
var array = [1, 2, 3]

array.sort{(item1 : Int, item2: Int) -> Bool in return item1 < item2 }

//省略参数类型  由array的类型推算
array.sort(by: {(item1, item2) -> Bool in return item1 < item2 })

//省略闭包返回值,由 in 后面的函数体推算
array.sort(by: {(item1, item2) in return item1 < item2 })

//尾随闭包写法
array.sort{(item1, item2) in item1 < item2 }

//省略参数声明,由闭包内嵌参数代替
array.sort{ return $0 < $1 }

//省略 return 关键字
array.sort{ $0 < $1 }

//省略 参数
array.sort(by: <)

捕获值

这里借助官方文档中的列子

func makeIncrementer() -> () -> Int {
    var runningTotal = 10
    func incrementer() -> Int {
        runningTotal += 1
        return runningTotal
    }
    return incrementer
}

let makeInc = makeIncrementer()
print(makeInc())
print(makeInc())
print(makeInc())
11
12
13
Program ended with exit code: 0

runningTotal是一个临时变量,每次进来的时候讲道理应该是10,但是这里的输出是一直累加的,原因是底层调用了swift_allocObject,在堆上分配了一个空间,在内嵌函数使用调用完成后释放

总结

  • 一个闭包能够从上下文捕获已被定义的常量和变量。即使这些变量和变量的员作用域已经不存在了,闭包仍能够在其函数体内引用和修改这些值。
  • 当我们每次修改捕获值的时候,修改的是堆区中的值
  • 当每次重新执行当前函数时候,都会为捕获值重新创建内存空间
捕获值的方式
  • 自动捕获
var age = 10
let closure = {
   age += 1
}
closure()
print(age)
11
Program ended with exit code: 0
  • 手动捕获
var age = 10
let closure = { [age] in
   print("闭包内:\(age)")
}
age = 11
closure()
print("闭包外:\(age)")
闭包内:10
闭包外:11
Program ended with exit code: 0

总结
自动捕获的参数可以修改,并且影响外部变量
手动捕获的参数本质是值类型,不可修改,跟外部变量没关系了

逃逸闭包

逃逸闭包的定义:当闭包作为一个实际参数传递给一个函数的时候,并且是在函数返回之后调用,我们就说这个闭包逃逸了,在闭包参数前些@escaping来明确闭包是允许逃逸的。
Swift 3.0之后,系统默认闭包参数是@nonescaping

逃逸闭包一般有两种实现

  • 延迟调用
  • 先存储,在后面进行调用

先存储,在后面进行调用举例:

class LGTeacher{

    var complitionHandler: ((Int)->Void)?

    func makeIncrementer(amount: Int,  handler: @escaping (Int) -> Void){
        var runningTotal = 10
        runningTotal += amount
        self.complitionHandler = handler
    }

    func doSomething(){
        self.makeIncrementer(amount: 10) {
            print($0)
        }
    }

    deinit {
        print("LGTeaher deinit")
    }

}

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()

        let t = LGTeacher()

        t.doSomething()

        t.complitionHandler?(10)
    }

}
10
LGTeaher deinit

延迟调用举例:

class LGTeacher{

    var complitionHandler: ((Int)->Void)?

    func makeIncrementer(amount: Int,  handler: @escaping (Int) -> Void){
        var runningTotal = 10
        runningTotal += amount
//        self.complitionHandler = handler
        DispatchQueue.global().asyncAfter(wallDeadline: .now() + 0.1) {
            handler(runningTotal)
        }

    }

    func doSomething(){
        self.makeIncrementer(amount: 10) {
            print($0)
        }
    }

    deinit {
        print("LGTeaher deinit")
    }

}

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()

        let t = LGTeacher()

        t.doSomething()

        t.complitionHandler?(10)
    }

}
10
LGTeaher deinit

逃逸闭包循环引用

非逃逸闭包是不会产生循环引用的,上下文保存栈上,而不是堆上

class LGStudent{
    var age:Int = 18
}

class LGTeacher{
    
    var student = LGStudent()
    

    var complitionHandler: (()->Void)?

    func makeIncrementer(handler: @escaping () -> Void){
        self.complitionHandler = handler
    }

    func doSomething(){
        self.makeIncrementer{
            print(self.student.age)
        }
    }

    deinit {
        print("LGTeaher deinit")
    }

}

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()

        let t = LGTeacher()

        t.doSomething()

        t.complitionHandler?()
    }

}
18

只打印了18,没有打印LGTeaher deinit

使用[weak self] 或者 [unowned self]解决循环引用

self.makeIncrementer{ [weak self] in
    print(self?.student.age)
}
self.makeIncrementer { [weak student] in
    print(student?.age)
}
self.makeIncrementer { [unowned self] in
    print(self.student.age)
}
self.makeIncrementer { [unowned student] in
    print(student.age)
}
18
LGTeaher deinit

总结

  • 在捕获变量时,由于数据会copy一份,会影响对象的引用计数,使用weak或者unowned修饰,不会影响引用计数
  • 使用weak修饰时,会将捕获的值变为可选类型,有可能为空
  • 使用unowned,修饰时要确保对象存在,如果对象被释放,会出现野指针
  • 非逃逸闭包不会造成循环引用,比如系统的动画闭包,比如snp布局闭包等

相关文章

  • Swift-闭包

    Swift 闭包 函数 ()->() Swift 中的闭包和 Objective-C 中的 block 类似,闭包...

  • Swift闭包和函数

    函数在Swift中只是一种特殊的闭包,闭包在Swift语言中是一等公民,支持闭包嵌套和闭包传递。Swift中的闭包...

  • swift4 闭包

    swift 闭包 闭包:swift 中 函数是闭包的一种类似于oc的闭包闭包表达式(匿名函数) -- 能够捕获上下...

  • Swift中的闭包

    在Swift中有两种闭包,逃逸闭包(@escaping)和非逃逸闭包(@nonescaping)。从Swift 3...

  • 100 Days of Swift - Day 06 - 闭包(

    100 Days of Swift - Day 06 - 闭包Closures 6.1 闭包 Swift函数也属于...

  • swift学习

    * 闭包 * 闭包作为属性 ```swift // 声明闭包类型 typealias callba...

  • iOS swift 逃逸闭包(@escaping)和非逃逸闭

    iOS swift 逃逸闭包(@escaping)和非逃逸闭包 (@noescaping) 逃逸闭包: 逃逸闭包...

  • iOS&Swift&OC 闭包和Block的相互转化

    一、Swift的闭包 -> OC的block 二、OC的block -> Swift的闭包

  • swift闭包学习

    闭包作为参数 参考 Swift学习之闭包

  • Swift学习笔记(1)

    SWift学习笔记 闭包 闭包表达式 闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 ...

网友评论

      本文标题:Swift 闭包

      本文链接:https://www.haomeiwen.com/subject/jkpsaktx.html