关于swift:弱引用和无引用引用之间有什么区别?

What is the difference between a weak reference and an unowned reference?

斯威夫特:

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

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

何时可以安全地使用无主引用?

未经引用的引用是否像C/C++中的挂起指针一样的安全风险?


weakunowned引用都不会在引用的对象上创建strong保留(也就是说,它们不会增加保留计数,以防止arc重新分配引用的对象)。

但为什么有两个关键词?这种区别与Optional类型内置于Swift语言的事实有关。长话短说:可选类型提供了内存安全性(这与Swift的构造规则很好地配合使用——这是为了提供这种好处而严格执行的)。

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

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

来自苹果文档:

Use a weak reference whenever it is valid for that reference to become
nil at some point during its lifetime. Conversely, use an unowned
reference when you know that the reference will never be nil once it
has been set during initialization.

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

weak关键字示例:

1
2
3
4
5
6
7
8
9
10
11
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艺术(您应该去看看文档-它们有漂亮的图表):

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

PersonApartment示例显示了这样一种情况,即两个属性都允许为零,这两个属性都有可能导致强烈的参考循环。这种情况最好用弱引用来解决。两个实体都可以存在,而不必严格依赖于另一个实体。

unowned关键字示例:

1
2
3
4
5
6
7
8
9
10
11
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属性。

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

CustomerCreditCard示例显示了这样一种情况:一个允许为零的属性和另一个不允许为零的属性有可能导致强烈的参考循环。最好使用无主引用解决此方案。

苹果公司的说明:

Weak references must be declared as variables, to indicate that their
value can change at runtime. A weak reference cannot be declared as a
constant.

还有第三种情况,两个属性都应该始终有一个值,一旦初始化完成,两个属性都不应该为零。

在处理闭包时,也有一些经典的保留周期场景可以避免。

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


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

弱引用:

A weak reference is a reference that does not keep a strong hold on
the instance it refers to, and so does not stop ARC from disposing of
the referenced instance. Because weak references are allowed to have
"no value", you must declare every weak reference as having an
optional type. (Apple Docs)

无主参考:

Like weak references, an unowned reference does not keep a strong hold
on the instance it refers to. Unlike a weak reference, however, an
unowned reference is assumed to always have a value. Because of this,
an unowned reference is always defined as a non-optional type. (Apple Docs)

每次使用时间:

Use a weak reference whenever it is valid for that reference to become
nil at some point during its lifetime. Conversely, use an unowned
reference when you know that the reference will never be nil once it
has been set during initialization. (Apple Docs)

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

如上所述,假设无主引用始终具有值。所以,只有当您确信引用永远不会为零时,才应该使用它。Apple Docs通过下面的例子说明了一个无主引用的用例。

假设我们有两个类:CustomerCreditCard。客户可以没有信用卡存在,但是没有客户信用卡就不存在,也就是说,可以假定信用卡总是有客户。因此,他们应该有以下关系:

1
2
3
4
5
6
7
class Customer {
    var card: CreditCard?
}

class CreditCard {
    unowned let customer: Customer
}

Q3。是"未拥有的引用"引用C/C++中的"悬空指针"之类的安全风险

我不这么认为。

由于无主引用只是保证有价值的弱引用,因此不应以任何方式构成安全风险。但是,如果在解除分配它引用的实例之后尝试访问一个无主引用,则会触发一个运行时错误,应用程序将崩溃。

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

链接到Apple文档


如果self在闭包时可以为零,则使用[弱self]。

如果self在关闭时永远不会为零,请使用[无主self]。

如果当你使用[无主的自我]时它崩溃了,那么在这个结束的某个时刻,自我可能是零的,你可能需要使用[弱的自我]来代替。

查看有关在闭包中使用强、弱和无主的示例:

https://developer.apple.com/library/ios/documentation/swift/conceptive/swift_programming_language/automaticreferencecounting.html


从链接提取

很少有结论

  • 确定你是否需要担心强、弱或无主,问,"我在处理引用类型吗?"如果你在工作对于结构或枚举,arc不管理这些类型的内存。你甚至不必担心指定软弱或无主那些常数或变量。
  • 在父级引用子级的层次关系中,强引用是很好的,反之亦然。事实上,在大多数情况下,强引用是最受欢迎的一种引用。
  • 当两个实例可选地相互关联时,请确保其中一个实例对另一个实例的引用很弱。
  • 当两个实例以这样一种方式关联时,实例不能没有其他实例,具有强制依赖关系需要保留对另一个的无主引用实例。

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

强的

它本质上是一个正常的引用(指针和全部),但它本身的特殊性在于,它通过增加保留计数1来保护被引用对象不被arc释放。本质上,只要任何事物对某个对象具有强引用,它就不会被释放。

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

解析类实例之间的强引用循环

使用weakunowned变量的重要地方是在存在潜在保留周期的情况下。保留周期是两个对象彼此具有强引用时发生的情况。如果两个对象彼此具有强引用,则ARC不会在每个实例上生成适当的发布消息代码,因为它们使彼此保持活动状态。

弱的

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

ARC automatically sets a weak reference to nil when the instance that it refers to is deallocated. And, because weak references need to allow their value to be changed to nil at runtime, they are always declared as variables

使用

在允许两个属性都是nil的情况下。这种情况最好用weak参考来解决。

当另一个实例的生存期较短时(即当可以首先释放另一个实例时),使用weak引用。

无人拥有的

无主引用和弱引用一样,不会增加被引用对象的保留计数。无主引用不归零。这意味着当对象被释放时,它不会使指针归零。这意味着在某些情况下,使用无主引用会导致指针悬空。

ARC never sets an unowned reference’s value to nil, which means that unowned references are defined using non-optional types.

< BR>

IMPORTANT.

Use an unowned reference only when you are sure that the reference always refers to an instance that has not been deallocated.

If you try to access the value of an unowned reference after that instance has been deallocated, you’ll get a runtime error - Attempted to read an unowned reference but object was already deallocated

< BR>

NOTE

Swift also provides unsafe unowned references for cases where you need to disable runtime safety checks—for example, for performance reasons. As with all unsafe operations, you take on the responsibility for checking that code for safety.

You indicate an unsafe unowned reference by writing unowned(unsafe). If you try to access an unsafe unowned reference after the instance that it refers to is deallocated, your program will try to access the memory location where the instance used to be, which is an unsafe operation.

使用

在一种情况下,允许一种财产为nil,另一种财产不能为nil。这种情况最好用unowned参考来解决。

当另一个实例具有相同的生存期或更长的生存期时,使用unowned引用。就像一个隐式的未包装的选项一样,如果您可以保证引用在其使用点不会是nil,那么使用unowned。如果没有,那么您应该使用weak

强烈建议阅读文档和源


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

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

1
2
3
4
5
6
7
8
9
10
11
12
class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

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


摘自乔恩·霍夫曼的《掌握斯威夫特4》一书:

The difference between a weak reference and an unowned reference is that the instance which a weak reference refers to can be nil, whereas the instance that an unowned reference is referring to cannot be nil. This means that when we use a weak reference, the property must be an optional property, since it can be nil.


当您确定在您访问self时,self永远不能是nil时,使用unowned

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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()
    }
}

当您访问self时,有可能self可以是nil时,使用weak

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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上问一下,你应该怎么做!在不应该使用弱代码的时候一直使用它,这对您和代码的读者来说都是一种困惑。