iOS Swift No.24 - 自动引用计数3

第二十四章 自动引用计数

4.2 Unowned Optional Reference (无主可选引用)

我们可以把通向类的可选引用看作无主可选引用。在ARC关系模型中,一个无主可选引用和一个弱引用都可以使用在相同的上下文中。两者之间使用不同之处就在于,当我们在使用无主可选引用的时候,我们就可以确定将要引用的对象是有效或者该对象被设置为nil了。

下面这个例子,就是追踪学校中部门所授予的课程信息。

class Department {
    var name: String
    var courses: [Course]
    init(name: String) {
        self.name = name
        self.courses = []
    }
}
class Course {
    var name: String
    unowned var department: Department
    unowned var nextCourse: Course?
    init(name: String, in department: Department) {
        self.name = name
        self.department = department
        self.nextCourse = nil
    }
}

Department保留了一个强引用,用于部门授权的所有课程,在ARC关系模型中,一个部门拥有很多个课程,Course有两个无主引用,一个是指向部门的,另一个是指向学生应该上的下一个课程。 课程并不能拥有任何一个对象。每一门课程都属于相同的部门,所以Department属性并不是可选的,然而有些课程并不是建议的后续课程,所以nextCourse属性是可选的。

下面是使用这些类的例子

let department = Department(name: "Horticulture")

let intro = Course(name: "Survey of Plants", in: department)
let intermediate = Course(name: "Growing Common Herbs", in: department)
let advanced = Course(name: "Caring for Tropical Plants", in: department)

intro.nextCourse = intermediate
intermediate.nextCourse = advanced
department.courses = [intro, intermediate, advanced]

上面的代码创建了一个部门和三个课程,其中基础和中级课程都有一个存储在nextCourse属性中建议下一门课程。nextCourse保留的是一个无主可选引用,这就意味着在学生完成上一门课程后所上的下一门课程。
在这里插入图片描述

无主可选引用并不会强烈地保留在它所包装的类的某个实例,所以无主引用并不会阻止该实例的释放。所以它和无主引用的行为作用是一样的,除了无主可选引用可以是nil的情况外。

就像非可选无主引用(non-optional unowned reference)那样,我们要确保为nextCourse属性所引用的课程并没有被释放所负责,举个例子在如此情况下,但我们在department.course中删除一个课程,我们就得移除其他了能拥有该课程的引用。

在这里插入图片描述

4.3 Unowned Reference and Implicitly Unwrapped Optional Properties (无主引用和隐式展开可选属性)

上面的弱引用和无主可选引用涵盖了两个需要断开强引用循环的情况。

Person和Department例子展示了两个属性的值都允许是nil,所以这种情况就有可能会造成强引用循环。所以在这种情况下最好的解决方法就是使用弱引用。

Customer和CreditCard例子展示了一个属性可以允许是nil,而另一个属性不能是nil的情况。所以这种情况也是有可能造成强引用循环,所以在这种情况下做好的解决方法就是使用无主引用。

然而,还有第三种情况,就是两个属性都应该有一个值,且任何一个属性在初始化之后都不能是nil,所以说在这第三种情况下,我们就要使用一个类使用无主属性,另一个类使用隐式站来可选属性相结合起来的方法了。

这就会使两个属性在完成初始化之后可以被直接读取(不需要可选展开)。同时也会避免引用循环的发生。接下来的这个章节将会具体说明这种关系是如何建立的。

下例定义了两个类CountryCity,每个类将另外一个类的实例存储为属性,在这个数据模型中,每一个国家都有有一个首都城市,并且每一个城市都要属于这个国家。所以为了表达这层关系,类Country要有一个City属性,而类City也要有一个Country属性,你中有我,我中有你的关系。

class Country {
    let name: String
    var capitalCity: City!
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}
class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}

为了设置这两个类之间的相互依赖关系,City的构造器使用的是一个Country实例,并且把该实例存储在country属性里。Country的构造器调用了City的构造器。只有新的Country实例完全初始化好之后,Country的构造器才能将self传递给City的构造器。详情见两段构造过程。

来处理这种请求。我们可以将Country中的capitalCity定义为一个隐式展开可选属性。并且在类型注释的后面用叹号指明这时一个隐式展开可选属性,这就意味着capitalCity属性就像其他可选类型那样有一个默认值nil,但不需要展开它的值就能直接读取,详情见隐式展开可选。

因为capitalCity有一个nil的值,只有Country的实例在它的构造器中设置了name属性之后,一个新的Country实例才会考虑完全构造完成。这就意味着 此时Country构造器可以开始引用和传递隐含的self属性,只要那么name属性一旦设置完成。因此Country构造器可以传递self作为City构造器参数的一部分。当Country构造器设置了它自己的capitalCity属性之后。

所有的这一切意味着我们可以用一个语句来创建Country和City实例。而并不会产生强引用循环,capitalCity属性可以被直接读取,而并不需要用叹号来展开它的可选值。

var country = Country(name: "Canada", capitalName: "Ottawa")
print("\(country.name)'s capital city is called \(country.capitalCity.name)")
// Prints "Canada's capital city is called Ottawa"

上面的这个例子,使用的是隐式展开可选意味着满足了类的两段构造器要求。在构造过程完成之后,可以和其他非可选值那样读取和使用capitalCity属性,而有能避免强引用循环的产生。

猜你喜欢

转载自blog.csdn.net/weixin_45026183/article/details/107704779