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


240

斯威夫特有:

  • 强引用
  • 参考文献薄弱
  • 无人参考

无主引用与弱引用有何不同?

什么时候可以使用无主引用安全?

无主引用是否像C / C ++中的悬空指针一样具有安全风险?



我的经验是unowned用于我们控制的类,用于Apple的类,weak因为我们无法保证它能做什么
onmyway133 2015年

@NoorAli,或作为“无主”引用的“ ownBy”通常指向所有者。
伊恩·林罗斯

Answers:


361

双方weakunowned引用不创建一个strong被引用的对象上保持(又名它们不会取消分配引用的对象增加,为了保留计数,以防止电弧)。

但是为什么要两个关键词呢?这种区别与Optional类型内置在Swift语言中这一事实有关。长话短说:可选类型提供了内存安全性(这与Swift的构造函数规则完美结合-为了提供这种好处而严格)。

一个weak参考允许它可能成为nil(这个自动引用对象被释放情况),因此你的财产的类型必须是可选的-所以你,作为一个程序员,有义务使用它(基本上是前检查编译器会尽力迫使您编写安全的代码)。

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

Apple文档

只要有效的引用在其生存期内的某个时候变为零就使用弱引用。相反,当您知道在初始化期间将其设置为绝对不会为零时,请使用无主引用。

在文档中,有一些示例讨论了保留周期以及如何打破它们。所有这些示例均摘自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艺术(您应该去看一下文档 -它们有漂亮的图表):

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

在此示例中,一个a Customer可能有也可能没有CreditCard,但是a CreditCard 将始终与a关联Customer。为了表示这一点,Customer该类具有可选card属性,但是CreditCard该类具有非可选(且没有customer所有权)属性。

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

CustomerCreditCard实施例示出了其中被允许是nil一个属性并且不能是零另一个属性具有引起很强的参考周期的电势的情况。最好使用无主引用来解决此情况。

苹果的注意事项:

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

还有第三种情况,即两个属性都应始终具有值,并且初始化完成后,两个属性都不应为零。

此外,在使用闭包时,还可以避免经典的保留周期方案。

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


3
这有点琐碎,但我发现Apartment and Person的示例有些令人困惑,这也提出了打破强参考周期的其他解决方案。一个人的公寓是可选的,因此可以为零,而公寓的房客是可选的,因此可以为零,因此这两个属性都可以定义为弱。```
Justin Levi 2014年

类Person {让名字:String init(name:String){self.name = name} weak var公寓:公寓?}类公寓{让数字:Int初始化(数字:Int){self.number =数字}弱var租户:人?}
Justin Levi 2014年

3
之间有什么区别 weak var Person?vs.有var Person?什么?
院长

4
@JustinLevi,如果将两个属性都声明为弱,则有可能将它们释放。该人对公寓有很强的参考,因此公寓不会被释放。如果该单元对Person具有相同的强引用,则它们将创建一个保留周期-如果程序员知道该保留周期,则程序员可以在运行时打破该保留周期,否则,这仅仅是内存泄漏。这就是强,弱和无主的大惊小怪:更高级别的内存管理,因为ARC为我们完成了所有肮脏的工作。避免保留周期是我们的工作。
Ilea Cristian 2014年

1
弱者比弱者的唯一好处是您不需要解包并可以使用常量吗?是否有您无法使用弱项而只能使用无主项的情况?
艾伦(Alan)

29

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

参考不足:

弱引用是指不会对其引用的实例保持强大控制的引用,因此不会阻止ARC处置所引用的实例。因为弱引用允许具有“无值”,所以您必须将每个弱引用声明为具有可选类型。(Apple文件)

匿名参考:

像弱引用一样,无主引用也不会对其引用的实例保持强大的控制力。但是,与弱引用不同,假定无主引用始终具有值。因此,始终将无主引用定义为非可选类型。(Apple文件)

何时使用每个:

只要有效的引用在其生存期内的某个时候变为零就使用弱引用。相反,当您知道在初始化期间将其设置为绝对不会为零时,请使用无主引用。(Apple文件)


Q2。什么时候可以使用“非公开引用”?

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

假设我们有两个类CustomerCreditCard。客户可以不使用信用卡而存在,但是如果没有客户,则信用卡将不存在,即可以假设信用卡将始终具有客户。因此,它们应具有以下关系:

class Customer {
    var card: CreditCard?
}

class CreditCard {
    unowned let customer: Customer
}

Q3。是“无主引用”引用的安全风险,例如C / C ++中的“悬空指针”

我不这么认为。

由于无主引用只是保证具有值的弱引用,因此无论如何都不应构成安全风险。但是,如果在释放引用后的实例后尝试访问未拥有的引用,则会触发运行时错误,并且应用程序将崩溃。

这是我看到的唯一风险。

链接到Apple文档


您的Q2示例程序易于理解,没有所有权..谢谢..您可以为弱者和强者添加相同类型的示例吗?
Ranjith Kumar

优秀的。谢谢。
Swifty McSwifterton

您能列举一个无人或弱者的常见例子吗?
Honey

考虑对象父级和子级,如果没有父级不能存在unowned子级,则在子类中将其用于父级的属性。反之亦然。很好的解释@myxtic! unowned引用只是weak保证具有值的引用!
赛义夫

26

如果self在封闭中可能为零,请使用[weak self]

如果self在封闭中永远不会为零,请使用[unown self]

如果在使用[unown self]时崩溃,则self在该闭包中的某个时刻可能为零,您可能需要使用[weak self]来代替。

查看有关在闭包中使用strongweakunowned的示例:

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


7
为什么即使自我永远不能为零,也不要只使用弱者,对自己没有坏处呢?
Boon

4
嗨@Boon-这的确是关键问题。
Fattie

[弱自我] =>如果我在viewDidLoad()中使用闭包,怎么可能self为零?
哈桑·塔雷克

@HassanTareq,我认为上面提到的文章中提到了几个很好的例子。检查“解决封闭的强参考循环”部分,尤其是。Quote:“ Swift要求您每次在闭包中引用self成员时都编写self.someProperty或self.someMethod()(而不是someProperty或someMethod())。这可以帮助您记住,可以通过捕获来捕获self事故。” 摘自:Apple Inc.“ Swift编程语言(Swift 4)”。iBooks的。itunes.apple.com/de/book/the-swift-programming-language-swift-4/...
尼克Entin

1
@Boon如果始终使用弱函数,则编译器将在使用前强制检查是否为可选。如果您没有检查,它将给出编译时错误。没有其他危害。
维卡斯·米什拉

5

链接摘录

结论点很少

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

1

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

也就是说,在您确定对象的生存期比引用的生存期更长的情况下,使用无所有权的引用是安全的。如果不是这种情况,最好使用弱引用。

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

现在,悬空的指针指向已被释放的内存位置。但是要迅速处理,因为只要对对象有一个未拥有的引用,就只能将其释放,因此不会导致指针悬空。

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


0

当一个对象仅应由另一个对象拥有时,无主引用是在两个对象之间具有相同生命周期关系的情况下使用的一种弱引用。这是在对象及其属性之一之间创建不可变绑定的一种方法。

在中间快速WWDC视频中给出的示例中,一个人拥有一张信用卡,而一张信用卡只能有一个持有人。在信用卡上,此人不应是可选财产,因为您不想让只有一位所有者的信用卡随处可见。您可以通过将贷方的持有人属性设为弱引用来打破此循环,但这还要求您将其设为可选以及变量(而不是常量)。在这种情况下,无主引用意味着,尽管CreditCard在某个Person中没有拥有的股份,但其寿命取决于它。

class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

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

链接到wwdc视频或标题?
Osa

-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上问您应该怎么做!在不该使用的所有时间,只会使您和您的代码读者感到困惑。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.