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

问题描述 投票:219回答:8

斯威夫特有:

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

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

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

无主参考是否存在像C / C ++中的dangling pointers这样的安全风险?

swift memory-management automatic-ref-counting weak-references dangling-pointer
8个回答
332
投票

weakunowned引用都不会在引用的对象上创建strong保持(a.k.a.它们不会增加保留计数以防止ARC释放引用的对象)。

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

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

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

来自Apple docs

只要对该引用有效,就使用弱引用在其生命周期的某个时刻变为nil。相反,当您知道在初始化期间设置引用后永远不会为nil时,请使用无主引用。

在文档中有一些例子讨论了保留周期以及如何打破它们。所有这些例子都是从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示例显示了两个属性(均允许为零)的情况,可能会导致强大的参考周期。使用弱引用可以最好地解决此方案。两个实体都可以存在,而不必严格依赖另一个实体。

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示例显示了一种情况,其中一个属性被允许为零,另一个属性不能为零,有可能导致强引用循环。使用无主引用可以最好地解决此方案。

Apple的注意事项:

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

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

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

为此,我鼓励您访问Apple docs,或阅读the book


26
投票

Q1。 “无主参考”与“弱参考”有何不同?

弱参考:

弱引用是一种引用,它不会强制保留它引用的实例,因此不会阻止ARC处理引用的实例。因为弱引用被允许具有“无值”,所以必须将每个弱引用声明为具有可选类型。 (Apple Docs)

无主参考:

与弱引用一样,无主引用并不会强烈保留它所引用的实例。然而,与弱引用不同,假定无主引用始终具有值。因此,无主引用始终定义为非可选类型。 (Apple Docs)

何时使用每个:

只要对该引用有效,就使用弱引用在其生命周期的某个时刻变为nil。相反,当您知道在初始化期间设置引用后永远不会为nil时,请使用无主引用。 (Apple Docs)


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

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

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

class Customer {
    var card: CreditCard?
}

class CreditCard {
    unowned let customer: Customer
}

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

我不这么认为。

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

这是我看到的唯一风险。

Link to Apple Docs


25
投票

如果封闭使用自我可能是零[弱自我]。

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

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

查看关于在闭包中使用strong,weak和unowned的示例:

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


4
投票

来自link的提取物

结论要点很少

  • 要确定您是否需要担心强,弱或无主,请问“我是否在处理参考类型”。如果您正在使用Structs或Enums,ARC不会为这些类型管理内存,您甚至不必担心为这些常量或变量指定弱或无内容。
  • 在父级引用子级的层次关系中,强引用很好,但反之亦然。实际上,强引用是大多数时候最适合的参考。
  • 当两个实例可选地彼此相关时,请确保其中一个实例对另一个实例保持弱引用。
  • 如果两个实例相关联,其中一个实例在没有另一个实例的情况下不存在,则具有强制依赖关系的实例需要保持对另一个实例的无主引用。

0
投票

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

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

class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

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

0
投票

ARC是一个编译时功能,是Apple的自动内存管理版本。它代表自动参考计数。这意味着当对象没有强引用时,它只释放对象的内存。

强大

它本质上是一个普通的引用(指针和全部),但它本身特别之处在于它通过将保留计数增加1来保护被引用对象不被ARC解除分配。本质上,只要任何东西都有强引用一个对象,它不会被释放。

通常,当对象的层次关系是线性的时,我们可以安全地使用强引用。当强引用的层次结构从父级传递到子级时,使用强引用总是可以的。

解决类实例之间的强引用周期

使用weakunowned变量的重要位置是在您有潜在保留周期的情况下。当两个对象都具有相互强引用时,会发生保留周期。如果2个对象彼此具有强引用,则ARC将不会在每个实例上生成相应的释放消息代码,因为它们彼此保持活动状态。

弱引用只是指向对象的指针,该对象不保护对象不被ARC释放。虽然强引用会将对象的保留计数增加1,但弱引用则不会。此外,弱引用在成功解除分配时将指针归零。这确保了当您访问弱引用时,它将是有效对象或nil

当引用它的实例被解除分配时,ARC会自动将弱引用设置为nil。并且,因为弱引用需要允许它们的值在运行时更改为nil,所以它们总是被声明为变量

运用

在两个属性的情况下,两个属性都允许是nil。使用weak引用可以最好地解决此方案。

当另一个实例的生命周期较短时,即当另一个实例可以首先被释放时,请使用weak引用。

无主

无主引用(如弱引用)不会增加被引用对象的保留计数。无主引用是非归零的。这意味着当对象被释放时,它不会将指针清零。这意味着在某些情况下,使用无主参考可以导致dangling pointers

ARC从未将无主引用的值设置为nil,这意味着使用非可选类型定义无主引用。

重要。

仅当您确定引用始终引用尚未释放的实例时,才使用无主引用。

如果在释放该实例后尝试访问无主引用的值,则会出现运行时错误 - Attempted to read an unowned reference but object was already deallocated

注意

对于需要禁用运行时安全检查的情况,Swift还提供不安全的无主参考 - 例如,出于性能原因。与所有不安全的操作一样,您负责检查该代码的安全性。

您通过编写unowned(unsafe)来表示不安全的无主参考。如果在取消分配引用的实例后尝试访问不安全的无主引用,则程序将尝试访问实例所在的内存位置,这是一种不安全的操作。

运用

在一个属性允许nil和另一个属性不能nil的情况下。使用unowned引用可以最好地解决此方案。

当另一个实例具有相同的生命周期或更长的生命周期时,请使用unowned引用。就像一个隐式展开的可选项,如果你可以保证引用在它的使用点不是nil,使用unowned。如果没有,那么你应该使用weak

强烈建议阅读docsource


-1
投票

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

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


-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

例:

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)
    }
}

unowned的缺点:

  • 比弱者更有效率
  • 你可以(好吧,你被迫)将实例标记为不可变(自Swift 5.0以来不再)。
  • 向读者表明你的代码:这个实例与X有关系,没有它就无法生存,但如果X消失了,我也走了。

weak的缺点:

  • 比没有人更安全(因为它不会崩溃)。
  • 可以创建一个双向关系的X关系,但两者都可以互相生存。

如果您不确定,请使用weak。等等,我的意思是在StackOverflow上问你在你的情况下应该做些什么!当你不应该使用弱时,只会让你和你的代码的读者感到困惑。

© www.soinside.com 2019 - 2024. All rights reserved.