Swift基础 闭包

闭包

闭包是用大括号括起来的,可以没有名字的函数类型的实例。闭包有三种形式:

  • 全局函数:具名函数,但不捕获任何值
  • 嵌套函数:在函数内部嵌套定义具名函数,可捕获包含函数中的值。
  • 闭包表达式:匿名函数类型的实例,不具名代码块,可捕获上下文中的值。

闭包是引用类型,闭包变量的拷贝具有引用语义。闭包和函数实例具有同样的内存模型。

表达式:

{
    
    (参数列表1,参数列表2,...)->(返回值) in
    函数体执行的内容
}

示例:

var closure = {
    
    (number1:Int,number2:Int)->Bool in
    if number1>number2 {
    
    
        return true
    }else{
    
    
        return false
    }
}
    
print("\(closure(1,2))")

运行结果:

false

闭包对上下文的值的捕获

函数类型和闭包可以捕获其所在上下文的值。

func addhandler(step:Int) -> (()->Int) {
    
    
    var sum = 0
    return {
    
    
        sum += step
        return sum
    }
}

如上例,如果该闭包函数类型的对象没有被销毁,那么该闭包对象就始终能捕获到sum变量。

//定义,未执行
let addByTen = addhandler(step: 10);
//执行并打印一次返回值
print("\(addByTen())")
print("\(addByTen())")
print("\(addByTen())")

运行结果:

10
20
30

从运行结果可以看出addByTen常量在其生存周期内,是一直持有自己闭包实例内的sum变量的,因此每次传入的step:10都可以与sum叠加。

若该闭包声明参数时的step声明为变量var,那么在闭包内也可以改变step的值,并且也被闭包捕获,每次调用闭包都会延续上一次被捕获的值的状态。

如果被捕获的值,其生存周期比闭包小,那么编译器就会将被捕获的值包装在一个临时对象里,然后在闭包对象上创建一个对象指针,指向该临时变量,一边闭包随时访问修改被捕获的值。

闭包的循环引用

class Person{
    
    
    var age:Int
    var name:String
    
    lazy var printName:() -> Void = {
    
    () -> Void in
        print("My name is " + self.name)
    }
    
    init(name:String,age:Int){
    
    
        self.name = name
        self.age = age
    }
    
    deinit{
    
    
        print("销毁")
    }
}

var xiaoMing:Person? = Person(name: "xiaoMing", age: 18)
xiaoMing?.printName()
//销毁xiaoMing的强引用,看会不会回收内存
xiaoMing = nil

运行结果:

My name is xiaoMing

当对象属性是一个闭包,而闭包里面又对self有引用的时候,这时就会产生循环引用,因为当self销毁的时候,闭包里面对self有引用,self的引用计数并不为0,所以self并不会被内存回收。

因此要解除这类闭包相关的循环引用,在闭包中使用self时,需要用weak关键字。

在调用了printName方法后,对象并没有被销毁,因为printName方法里的闭包对自身self有一个强引用,此时的引用计数为2,xiaoMIng = nil 之后,引用计数为1,并不为0,所以实际内存并没有被回收。

解决方式是,在闭包前,声明闭包内使用的self是无主引用self即可,如下方所示:

lazy var printName:() -> Void = {
    
    [unowned self]() -> Void in
    print("My name is " + self.name)
}

运行结果:

My name is xiaoMing
销毁

或者使用弱引用:

lazy var printName:() -> Void = {
    
    [weak self]() -> Void in
        print("My name is " + self!.name)
    }

效果是一样的。

对于循环引用的总结

引用的双方如果在其生命周期内可能会被置为nil,那就选用弱引用。这时其对象是一个可选类型。

如果对于被创建后就没有机会置nil的情况,就选用无主引用。使用无主引用有个好处,就是在闭包内使用self的时候不再是可选类型,也就不用带“?”了,可是一旦使用了无主引用就必须确保其不会为nil,一旦出现nil 的情况,就会导致系统奔溃。

猜你喜欢

转载自blog.csdn.net/kkkenty/article/details/124785586
今日推荐