Swift之weak和unowned

Swift 提供了两种方法解决你在使用类的属性而产生的强引用循环:弱引用( weak )和无主引用( unowned )。

弱引用( weak )和无主引用( unowned )能确保一个实例在循环引用中引用另一个实例,而 不用 保持强引用关系。这样实例就可以相互引用且不会产生强引用循环。

当一个实例的生命周期比较引用它的实例短,也就是这个实例可能会先于引用它的实例释放的时候,需要使用弱引用( weak )。对与一栋公寓来说在它的生命周期中是完全可以没有住户的,所以在这种情况下。相反,当一个实例拥有和引用它的实例相同的生命周期或是比引用它的实例更长的生命周期的时候,需要使用无主引用( unowned )。

一、弱引用 weak

  1. 弱引用创建出来的变量是可选的
  2. 弱引用设置为 nil 的时候不会触发属性观察者
class Student {
    var name: String = "student"
}

class Teacher {
    var name: String?
    // 弱引用属性时可选的
    weak var student: Student? {
        willSet {
            print("willSet student")
        }
        didSet {
            print("didSet student")
        }
    }
}

var student: Student? = Student()
var t = Teacher()
t.student = student
print(t.student)
print("--设置会触发属性观察 end--")
student = nil
print(t.student)
print("--不会触发属性观察 end--")
// 但是如果直接调用 t.student = nil 会触发属性观察
t.student = nil
print("--直接调用会触发属性观察 end--")
------
willSet student
didSet student
Optional(__lldb_expr_73.Student)
--设置会触发属性观察 end--
nil
--不会触发属性观察 end--
willSet student
didSet student
--直接调用会触发属性观察 end--

弱引用 不会强持有引用的实例,并且不会阻止 ARC 销毁引用的实例。这可以避免强引用循环。属性或是变量声明的前面加上 weak 关键词来表示这是弱引用。

弱引用多用于通常的解决循环引用问题场景。

二、无主引用 unowned

  1. 无主引用和弱引用类似,不会 retain 当前实例对象的引用,非可选,当一个对象被 unowned 标识之后,假定永远有值,非可选类型
  2. 非可选类型,访问时有 crash 风险
class Student {
    var name: String = "student"
}

class Teacher {
    var name: String?
    // 注意这里改成了unowned
    unowned var student: Student? {
        willSet {
            print("willSet student")
        }
        didSet {
            print("didSet student")
        }
    }
}

var student: Student? = Student()
var t = Teacher()
t.student = student
print(t.student as Any)
print("--设置会触发属性观察 end--")
student = nil
print(t.student)  // 会在这里崩溃,因为student已经为nil
print("--不会触发属性观察 end--")
// 但是如果直接调用 t.student = nil 会触发属性观察
t.student = nil
print("--直接调用会触发属性观察 end--")

像弱引用一样,无主引用 也不会对指向的对象持有强引用。但是,与弱引用不同的是,无主引用适用于其他实例有相同的生命周期或是更长的生命周期的场景。属性或是变量声明前面加上 unowned 关键字表示这是无主引用。

无主引用用于一个属性允许设置为 nil,而另一个属性不允许设置为 nil,并会造成潜在的强引用循环的场景。

比如下面:

class Student {
    var name: String = "student"
    unowned let teacher: Teacher
    
    init(name: String, t: Teacher) {
        self.name = name
        self.teacher = t
    }
    deinit {
        print("student deinit")
    }
}

class Teacher {
    var name: String?
    var student: Student?
    
    init(name: String) {
        self.name = name
    }
    deinit {
        print("teacher deinit")
    }
}

var t: Teacher? = Teacher(name: "teacherA")
t?.student = Student(name: "bill", t: t!)
t = nil
// Console out
teacher deinit
student deinit

三、解决闭包引起的强引用循环

定义闭包的时候同时定义 捕获列表 ,并作为闭包的一部分,通过这种方式可以解决闭包和实例之间的强引用循环。捕获列表定义了在闭包内部捕获一个或多个引用类型的规则。像解决两个实例的强引用循环一样,将每一个捕获类型声明为弱引用(weak)或是无主引用(unowned),而不是声明为强引用。至于是使用弱引用(weak)还是无主引用(unowned)取决于你代码中的不同部分之间的关系。

注意

Swift 强制要求
闭包内部使用 self 的成员,必须要写成 self.somePropertyself.someMethod() (而不是仅仅写成 somePropertysomeMethod())。这提醒你可能会一不小心就捕获了 self

  • 当闭包和它捕获的实例始终互相持有对方的时候,将闭包的捕获定义为无主引用,那闭包和它捕获的实例总会同时释放。

  • 相反的,将捕获定义弱引用时,捕获的引用也许会在将来的某一时刻变成 nil 。弱引用总是可选类型的,并且,当引用的实例释放的时候,弱引用自动变成 nil 。 这就需要你在闭包内部检查它的值是否存在。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章