弱引用和无主引用有什么区别?

9 浏览
0 Comments

弱引用和无主引用有什么区别?

Swift有:

  • 强引用(Strong References)
  • 弱引用(Weak References)
  • 无主引用(Unowned References)

无主引用和弱引用有什么不同?

在什么时候使用无主引用是安全的?

无主引用是否像C/C++中的悬垂指针(dangling pointers)一样存在安全风险?

admin 更改状态以发布 2023年5月23日
0
0 Comments

Q1. “Unowned reference”和“Weak Reference”有什么区别?

弱引用:

弱引用是一种引用,它不会对它所引用的实例保持强持有,所以不会阻止ARC处理被引用的实例。由于弱引用可以“没有值”,所以必须将每个弱引用声明为可选类型。(Apple Docs)

无主引用:

与弱引用类似,无主引用不会对其引用的实例保持强持有。但与弱引用不同的是,无主引用被认为总是有值的。因此,无主引用始终被定义为非可选类型。(Apple Docs)

何时使用:

在某个引用在其生命周期中的某个时间点上可以成为nil时,使用弱引用。相反,当您知道引用在初始化后永远不会为nil时,请使用无主引用。(Apple Docs)


Q2. 何时使用“无主引用”是安全的?

如上所述,无主引用被认为总是有值的。因此,只有在您确定引用永远不为nil时才应使用它。Apple Docs通过以下示例说明了无主引用的用例。

假设我们有两个类CustomerCreditCard。客户可以没有信用卡,但是信用卡将不会没有客户,即可以假设信用卡始终有一个客户。因此,它们应该具有以下关系:

class Customer {
    var card: CreditCard?
}
class CreditCard {
    unowned let customer: Customer
}


Q3. “无主引用”像C / C ++中的“悬垂指针”一样会有安全风险吗?

我认为不会。

由于无主引用只是保证有值的弱引用,因此它不应以任何方式构成安全隐患。但是,如果在所引用实例被销毁后尝试访问无主引用,则会触发运行时错误,应用程序将崩溃。

这是我看到的唯一风险。

链接到Apple Docs

0
0 Comments

weakunowned 引用都不会增加被引用对象的引用计数(也就是不会创建强引用),以此避免自动引用计数(ARC)过早释放被引用对象。

为什么需要两个关键字呢?这是因为 Optional 类型是 Swift 语言内置的。简单来说:可选类型 提供了内存安全性,这与 Swift 的构造器规则 完全契合,而这些规则即为了提供这种好处而严格执行。

weak 引用可能会变为 nil(当被引用对象被释放时就会变为 nil),因此你的属性类型必须是可选类型,作为程序员,你需要在使用前强制检测是否为空(基本上编译器会尽最大努力让你编写安全的代码)。

unowned 引用假设在其生命周期内永远不会变为 nilunowned 引用必须在初始化时设置,这意味着引用将被定义为非可选类型,可以安全地使用而无需进行检查。如果某个被引用的对象被释放了,那么当使用该 unowned 引用时,应用将会崩溃。

来自 Apple 文档 的描述:

当引用在其生命周期内可能会变为 nil 时,请使用弱引用。当您知道一旦在初始化之后引用就永远不会为 nil 时,请使用无主引用。

在文档中,还有一些讨论保留循环及如何破解它们的例子。所有这些例子都是从 文档 中提取的。

weak 关键字的例子:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
}
class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    weak var tenant: Person?
}

现在,来看一些 ASCII 艺术(你应该去看一下 文档 - 它们有非常棒的图表):

Person ===(strong)==> Apartment
Person <==(weak)===== Apartment

PersonApartment 的例子展示了这样一种情况:两个属性都允许为 nil,同时也有可能导致强引用循环。此时,最好使用弱引用。这两个实体都可以存在而不必直接依赖于对方。

unowned关键字的示例:

class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) { self.name = name }
}
class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) { self.number = number; self.customer = customer }
}

在这个例子中,一个Customer可能有也可能没有CreditCard,但CreditCard总是Customer相关联。为了表示这一点,Customer类有一个可选的card属性,但CreditCard类有一个非可选的(并且是未拥有的)customer属性。

Customer ===(strong)==> CreditCard
Customer <==(unowned)== CreditCard

CustomerCreditCard示例展示了这样的情况:一个允许为nil,而另一个不能为nil的属性可能会导致强引用循环。这种情况最好使用未拥有的引用来解决。

Apple的注释:

弱引用必须声明为变量,以表明它们的值在运行时可以更改。不能把弱引用声明为常量。

还有第三种情况,当两个属性应该始终拥有值,并且一旦初始化完成,任何一个属性都不应为空时。

还有在处理闭包时要避免的典型保留周期情况。

对此,我鼓励您访问Apple文档或阅读这本书

0