美文网首页
循环引用 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笔记

    Swift中weak与unowned的区别 在闭包里面为了解决循环引用问题,使用了 [unowned self]。...

  • Swift Weak与Unowned使用场合

    一、前言 以往在使用Swift中的weak和unowned关键字的时候的理解是,两者都会解决循环引用的问题,wea...

  • Swift中weak与unowned的区别

    在Swift的闭包中为了避免循环引用的问题,通常用[weak self] 或者[unowned self], 前者...

  • Swift 内存管理,weak 和 unowned

    在内存管理中,weak和unowned都可以防止发生引用循环问题,但是weak和unowned的区别在哪里呢? u...

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

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

  • Swift 内存管理之 weak 与 unowned

    在用 Swift 做开发时,我们可以使用 weak 或是 unowned 打破类实例和闭包的强引用循环。今天我们来...

  • RxSwift-内存管理

    一、循环引用 weak:弱引用,[weak self],需要解包操作,延迟调用为nil不崩溃 unowned:无主...

  • Swift 2 学习笔记 21.内存管理

    课程来自慕课网liuyubobobo老师 内存管理 deinit 引用计数 强引用循环和weak unowned ...

  • 浅明分析Swift循环引用

    看过不少分析Swift解决循环引用的文章,分析weak和unowned的区别等等,可能是不太符合我的思路,一直感觉...

  • unowned和weak的区别—swift

    weak引用和unowned引用有些类似但不完全相同。Unowned 引用,像weak引用一样,不会增加对象的引用...

网友评论

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

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