弱引用和无引用引用之间有什么区别?

时间:2014-06-03 09:27:39

标签: swift memory-management automatic-ref-counting weak-references dangling-pointer

斯威夫特有:

  • 强引用
  • 弱参考
  • 无主参考

无主参考如何与弱参考不同?

什么时候使用无主参考安全?

无主引用是否存在C / C ++中的dangling pointers等安全风险?

8 个答案:

答案 0 :(得分:326)

weakunowned引用都不会在引用的对象上创建strong暂停(也就是说,他们不会增加保留计数以防止ARC取消分配推荐对象)。

但为什么有两个关键词呢?这种区别与Optional类型内置Swift语言的事实有关。长话短说:optional types提供记忆安全(这与Swift's constructor rules完美配合 - 为了提供这种好处,这是严格的。)

weak引用允许它的可能性变为nil(当引用的对象被释放时会自动发生),因此你的属性的类型必须是可选的 - 所以你作为程序员,在使用它之前有义务检查它(基本上编译器强迫你尽可能地编写安全代码)。

unowned引用假定它在其生命周期内永远不会成为nil。必须在初始化期间设置无主引用 - 这意味着引用将被定义为非可选类型,可以安全地使用而无需检查。如果被引用的对象以某种方式被释放,那么当使用无主参考时,应用程序将崩溃。

来自Apple docs

  

只要对该引用有效,就使用弱引用   在其一生中的某个时刻没有。相反,使用无主   当你知道引用永远不会为零时引用   已在初始化期间设置。

在文档中有一些例子讨论了保留周期以及如何打破它们。所有这些示例都是从the docs中提取的。

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艺术(你应该去see the docs - 他们有漂亮的图表):

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的注意事项:

  

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

还有第三种情况,两个属性应始终具有值,并且一旦初始化完成,这两个属性都不应为nil。

还有一些经典的保留周期场景,以避免在使用闭包时。

为此,我们建议您访问Apple docs,或阅读the book

答案 1 :(得分:25)

<强> Q1。 “无主参考”与“弱参考”有什么不同?

弱参考:

  

弱引用是一种不能保持强势的引用   它引用的实例,因此不会阻止ARC处理   引用的实例。因为允许弱引用   “没有价值”,你必须声明每个弱引用都有   可选类型。 (Apple文档)

无主参考:

  

与弱引用一样,无主引用并不能保持强势   在它所指的实例上。然而,与弱参考不同,a   假定无主参考始终具有值。因为这,   无主引用始终定义为非可选类型。 (Apple文档)

何时使用每个:

  

只要对该引用有效,就使用弱引用   在其一生中的某个时刻没有。相反,使用无主   当你知道引用永远不会为零时引用   已在初始化期间设置。 (Apple文档)


<强> Q2。什么时候使用“无主参考”是安全的?

如上所述,假定无主参考始终具有值。因此,只有在确定引用永远不会为零时才应使用它。 Apple Docs通过以下示例说明了无主引用的用例。

假设我们有两个类CustomerCreditCard。客户可以在没有信用卡的情况下存在,但是没有客户就不会存在信用卡,即可以假设信用卡将始终拥有客户。所以,他们应该有以下关系:

class Customer {
    var card: CreditCard?
}

class CreditCard {
    unowned let customer: Customer
}

<强> Q3。 “无主参考”引用了C / C ++中的“悬空指针”等安全风险

我不这么认为。

由于无主引用只是保证具有值的弱引用,因此它不应该以任何方式存在安全风险。但是,如果您尝试在其引用的实例被取消分配后访问无主引用,则会触发运行时错误,应用程序将崩溃。

这是我看到它的唯一风险。

Link to Apple Docs

答案 2 :(得分:24)

如果 self 在闭包中可能为零,请使用 [弱自我]

如果 self 永远不会在闭包中使用 [无主自我]

如果你在使用 [unowned self] 时崩溃,那么在关闭的某个时刻self可能是零,你可能需要使用 [弱自我]

查看在闭包中使用无主的示例:

https://developer.apple.com/library/ios/documentation/swift/conceptual/swift_programming_language/AutomaticReferenceCounting.html

答案 3 :(得分:4)

link提取

结论要点很少

  • 确定您是否需要担心强弱,或弱 没有人,问,“我在处理参考类型”。如果你在工作 对于Structs或Enums,ARC不管理这些类型的内存 而你甚至不需要担心指定弱或无主 那些常数或变量。
  • 在父级引用子级的层次关系中,强引用很好,但反之亦然。事实上,大多数时候,强引用是最适合的参考。
  • 当两个实例可选择相互关联时,请确保 其中一个实例对另一个实例有弱引用。
  • 当两个实例以某种方式相关时 没有其他实例,实例不能存在 强制依赖需要保持对另一个的无主引用 实例

答案 4 :(得分:0)

无主引用是一种弱引用,用于两个对象之间的Same-Lifetime关系,当一个对象只能由另一个对象拥有时。这是一种在对象和它的一个属性之间创建不可变绑定的方法。

在中间swift WWDC视频中给出的示例中,一个人拥有一张信用卡,而一张信用卡只能有一个持有人。在信用卡上,此人不应该是可选的财产,因为您不希望只有一个所有者的信用卡。您可以通过使信用证上的持有者属性成为弱引用来打破此循环,但这也要求您使其成为可选和变量(而不是常量)。在这种情况下,无主参考意味着尽管CreditCard没有一个人拥有的股份,但它的生命依赖于它。

class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

    init (holder: Person) {
        self.holder = holder
    }
}

答案 5 :(得分:0)

weakstrong的引用都不会影响对象的引用计数。但是弱引用将始终是可选的,即可以为nil,而unowned引用永远不能为nil,因此它们将永远不是可选的。使用可选引用时,您将始终必须处理对象为nil的可能性。如果是无主引用,则必须确保该对象永远不会为零。使用对nil对象的未拥有的引用将类似于强制展开nil的可选对象。

这表示在确定对象的生存期比参考寿命更长的情况下,使用无所有权参考是安全的。如果不是这种情况,最好使用弱引用。

对于问题的第三部分,我不认为无主引用类似于悬挂指针。当我们谈论引用计数时,通常是指对象的强引用计数。同样,swift会为对象维护无主引用计数和弱引用计数(弱引用指向称为“边表”的东西,而不是对象本身)。当强引用计数达到零时,对象将被取消初始化,但是如果无主引用计数大于零,则无法将其释放。

现在,悬空的指针指向已被释放的内存位置。但是要迅速处理,因为只有在对对象有一个无用引用的情况下,才可以释放内存,因此不会导致指针悬空。

有很多文章更详细地讨论了快速内存管理。 Here是一个。

答案 6 :(得分:-1)

来自Jon Hoffman的书“Mastering Swift 4。”:

  

弱引用和无引用引用之间的区别在于弱引用引用的实例可以是nil,而无主引用所引用的实例不能为nil。这意味着当我们使用弱引用时,该属性必须是可选属性,因为它可以是nil。

答案 7 :(得分:-2)

请确保在您访问()的那一刻.*永远不会unowned时使用self

示例(您当然可以直接从nil添加目标,但这又是一个简单的示例)。:

self

在访问MyViewController时,class MyViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let myButton = MyButton { [unowned self] in print("At this point, self can NEVER be nil. You are safe to use unowned.") print("This is because myButton can not be referenced without/outside this instance (myViewController)") } } } class MyButton: UIButton { var clicked: (() -> ()) init(clicked: (() -> ())) { self.clicked = clicked // We use constraints to layout the view. We don't explicitly set the frame. super.init(frame: .zero) addTarget(self, action: #selector(clicked), for: .touchUpInside) } @objc private func sendClosure() { clicked() } } 可能是weak时,请使用self

示例:

nil

self的缺点:

  • 比弱要有效
  • 您可以(很好,您被迫)将实例标记为不可变的(自Swift 5.0起就不再可用)。
  • 向您的代码读者表明:此实例与X有关系,没有它就无法生存,但是如果X消失了,我也就消失了。

class MyViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() NetworkManager.sharedInstance.receivedData = { [weak self] (data) in print("Can you guarentee that self is always available when the network manager received data?") print("Nope, you can't. Network manager will be alive, regardless of this particular instance of MyViewController") print("You should use weak self here, since you are not sure if this instance is still alive for every") print("future callback of network manager") } } } class NetworkManager { static let sharedInstance = NetworkManager() var receivedData: ((Data) -> ())? private func process(_ data: Data) { // process the data... // ... eventually notify a possible listener. receivedData?(data) } } 的缺点:

  • 比没有人安全(因为它不会崩溃)。
  • 可以创建与X的双向关系,但是两者可以彼此独立。

如果不确定,请使用unowned等待,我的意思是在StackOverflow上问您应该怎么做!在不该使用的所有时间,只会使您和您的代码读者感到困惑。