美文网首页
循环引用 Swift中的weak和unowned关键字

循环引用 Swift中的weak和unowned关键字

作者: 贝灬小晖 | 来源:发表于2019-11-05 16:49 被阅读0次

    1.前言:
    循环引用

    循环引用是指两个或两个以上对象互相强引用,导致所用对象无法被释放的现象。这是内存泄露的一种情况

    有两个类,分别为爸爸(Father)和儿子(Son)。爸爸对儿子强引用,儿子对爸爸强引用。所以,要释放儿子必须先释放爸爸,要释放爸爸,则必须先释放儿子,如此一来,两个对象都无法被释放

    在OC中,可以很简单的举出一个循环引用的例子。比如有两个类A和B,A中有一个属性是B类的实例,而B中又有一个属性是A类的实例。同时这两个属性都是strong的,这就导致了一个最简单的循环引用。

    实例没有销毁,造成内存泄漏

    2.swift中的循环引用

    但是由于swift语法的特殊性,这样的例子不像OC中一样容易构造。因为对于一般类型的属性,Swfit要求在一个类的初始化方法中保证它一定有值。这将导致一个死循环。试想一下,A类在初始化的时候要保证它的某一个类型为B的属性先被初始化,而这个属性中又含有一个类型为A的属性需要先被初始化。
    这样循环下去的后果是,没有任何一个A或者B类的对象能先被初始化。如果允许代码的话,可以编译,但是运行时会报错:“EXC_BAD_ACCESS”.
    (简单的理解:当遇到了EXC_BAD_ACCESS异常,意味着访问了一个已经被释放的内存区域。)

    但是Swift这个特性并不意味着,在swift里面就不会出现引用循环问题了。因为swift还提供了可选类型,这个类型可以不被赋值,默认值就是nil

    与OC类似,解决循环引用问题最简单方法就是把属性定义为weak。

    类中循环引用
    所以可分为以下3中

    2.1. 如果产生循环引用的两个属性都允许为nil,这种情况适合用弱引用来解决
    随便哪一个可选类型的属性前面都可以加weak,但记住只要加一个就行了.

    class ClassA {
        weak var classBInstance: ClassB?
        init(){
            //初始化操作
        }
    }
    

    当弱引用所指向的对象被回收后,这个弱引用会自动被置为nil。这一点和OC非常类似。因此也可以看到,由于nil是可选类型的特权,所以weak修饰符仅能修饰可选类型属性。

    如果两个类 A 强引用 B B 弱引用A
    若B至为nil 则B 不会被deinit 此时打印 B为 nil 但A中的B依然存在
    若A至为nil 则A 将被deinit 此时打印 A为 nil 且B中的A也不存在

    两个类中,相互引用的两个属性都为可选类型,那么可以在一个属性的前面添加weak关键字,使该变量变为弱引用.


    image.png

    2.2 如果产生循环引用的两个属性一个允许为nil,另一个不允许为nil,这种情况适合用无主引用来解决

    只能在不能为nil的那个属性前面加unowned关键字,就是说 unowned设置以后即使它原来引用的内容已经被释放了,它仍然会保持对被已经释放了的对象的一个 "无效的" 引用,它不能是 Optional 值,也不会被指向 nil。如果尝试去调用这个引用的方法或者访问成员属性的话,程序就会崩溃.

    如下错误
    error: Execution was interrupted, reason: signal SIGABRT.

    image.png

    2.3. 如果产生循环引用的两个属性都必须有值,不能为nil,这种情况适合一个类使用无主属性,另一个类使用隐式解析可选类型

    隐式解析可选类型: 类似可选类型,默认值可以设置为nil
    两个属性一个在类型后面加!设置为隐式解析可选类型,另一个在属性前面加unowned关键字,设置为无主属性.

    与OC不同的是,除了弱引用外,swift还提供了无主引用来打破引用循环。根据我们刚刚的讨论,导致循环引用的属性,至少有一个是可选类型。这也就是说,有可能在另一个类里面,它的属性不是可选类型:

    class ClassB {
        unowned var classAInstance: ClassA = ClassA()
        init(){
            //初始化操作
        }
    }
    

    比如在B类中,classAInstance这个属性就可以不是可选类型。在这种情况下,还可以使用无主引用来打破引用循环。语法就是把weak替换为unowned关键字。unowned属性引用的对象被回收后,引用不会被置为nil,也不能被访问,否则会触发运行时错误

    闭包

    2.4闭包也是引用类型,怎么解决闭包的循环强引用

    闭包中对任何其他元素的引用都是会被闭包自动持有的。如果我们在闭包中写了 self 这样的东西的话,那我们其实也就在闭包内持有了当前的对象。这里就出现了一个在实际开发中比较隐蔽的陷阱:如果当前的实例直接或者间接地对这个闭包又有引用的话,就形成了一个 self -> 闭包 -> self 的循环引用

    使用 [unowned self]
    在闭包中定义一个捕获列表[unowned self],将self变为无主引用.这样就能够在避免产生循环强引用了

    lazy var group:() -> String = {        // 相当于一个没有参数返回string的函数
            [unowned self] in                   // 定义捕获列表,将self变为无主引用
            if let text = self.text {           // 解包
                return "\(self.name), \(text)"
            }else {
                return "\(self.name)"
            }
        }
    

    代码链接:

    https://github.com/IMKiller/swiftplayground

    1.使用

    2.意义

    weak 和 unowned 类似,不同点是 unowned 是永远有值的。weak可以声明可选型,很多时候我们不想声明一个可选型,可选型代表着风险,此时就可将属性声明成 unowned

    相关文章

      网友评论

          本文标题:循环引用 Swift中的weak和unowned关键字

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