简体   繁体   English

两个弱变量在Swift中相互引用?

[英]Two weak variables referencing each other in Swift?

I'm making another attempt today to try to understand retain cycles and weak references in Swift. 我今天再次尝试尝试理解Swift中的保留周期和弱引用。 Reading through the documentation , I saw the following code example where one of the referencing variables is marked as weak to prevent a retain cycle: 通过阅读文档 ,我看到了以下代码示例,其中一个引用变量被标记为weak以防止保留周期:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment? 
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    weak var tenant: Person?             // <---- This var is marked as 'weak'
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john

john = nil // prints "John Appleseed is being deinitialized"
unit4A = nil // prints "Apartment 4A is being deinitialized"

Is there any problem with making both variable weak? 让变量变弱都有问题吗? That is, in the Person class, could I change the apartment variable to be weak so that I have 也就是说,在Person类中,我可以将apartment变量更改为弱,以便我拥有

class Person {
    // ...
    weak var apartment: Apartment?  // added 'weak'
    // ...
}

class Apartment {
    // ...
    weak var tenant: Person?
    // ...
}

where there are two weak variables that reference each other. 其中有两个相互引用的弱变量。

I tested it out in the Playground and it seems to work ok, but is there any strong reason not to do this? 我在游乐场测试了它似乎工作正常,但有没有强烈的理由不这样做? It seems like in this case there is no natural parent-child relationship here. 在这种情况下似乎没有自然的亲子关系。

You can do that. 你可以做到这一点。 The only side effect is that you need to ensure that something else is retaining the people and the apartments. 唯一的副作用是你需要确保其他东西保留了人和公寓。 In the original code you just need to retain the people and the apartments (associated with people) will be retained for you. 在原始代码中,您只需要保留人员,公寓(与人相关)将保留给您。

Strictly speaking the people aren't killed when the apartments are demolished and the apartments aren't demolished when the people die so weak references in this scenario make sense. 严格来说,当公寓被拆除时人们没有被杀死,当人们死亡时公寓没有被拆除,因此这种情况下的弱参考是有道理的。 It's generally better to consider the relationship and ownership model you want and then decide how to achieve that. 通常最好考虑您想要的关系和所有权模型,然后决定如何实现这一目标。

To augment the accepted answer, here is a concrete example which demonstrates the behavior. 为了增加接受的答案,这里是一个演示行为的具体例子。

Try this is a Playground: 试试这是一个游乐场:

class Person {
    let name: String
    init(name: String) { self.name = name }
    weak var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    weak var tenant: Person?             // <---- This var is marked as 'weak'
    deinit { print("Apartment \(unit) is being deinitialized") }
}

class Test {
    var person: Person
    init() {
        person = Person(name: "Fred")
        let unit2B = Apartment(unit: "2B")
        person.apartment = unit2B
        unit2B.tenant = person
        print(person.apartment!.unit)
    }

    func test() {
        print(person.apartment!.unit)
    }
}

func go() {
    let t = Test()
    t.test()  // crashes here!
}

go()

At the time of init in class Test , the apartment that has been created is retained by the local variable unit2B . 在类Testinit时,已创建的公寓由本地变量unit2B保留。 When init is finished, the apartment will be deallocated because there are no longer any strong references holding it, so the program crashes when test is called because person.apartment is now nil . init完成时,公寓将被释放,因为不再有任何强引用,因此当调用test时程序崩溃,因为person.apartment现在nil

If you remove the weak from weak var apartment in class Person , then this example won't crash because the apartment created in init is retained by the person who is retained by the class property person . 如果从class Person weak var apartment删除weak ,则此示例不会崩溃,因为在init创建的单元由类属性person保留的person保留。

The other way to fix the example is to make unit2B be a property of class Test . 修复示例的另一种方法是使unit2B成为class Test的属性。 Then the apartment would have a strong reference holding it so unit2B wouldn't be deallocated after init . 然后公寓将有一个强大的参考保持它,所以unit2B将不会在init后解除分配。

If you remove weak from both weak var apartment in class Person and from weak var tenant in class Apartment , then the example won't crash, but neither the Person nor the Apartment will be deallocated because of the retain cycle created by two objects holding strong references to each other. 如果从class Person weak var apartmentclass Apartment weak var tenant中删除weak ,则该示例不会崩溃,但是由于两个保持强大的对象所创建的保留周期, PersonApartment都不会被释放相互引用。

Your question doesn't provide enough information for us to answer it. 您的问题没有提供足够的信息供我们回答。 You need to step back and study iOS memory management. 您需要退一步研究iOS内存管理。

The core concept is object ownership. 核心概念是对象所有权。 When you create an object and store a pointer to it in a strong variable, the system increments the retain count for that object. 创建对象并在强变量中存储指向它的指针时,系统会增加该对象的保留计数。 When the variable goes out of scope or you store nil into it, the system decrements the retain count. 当变量超出范围或将nil存储到其中时,系统会减少保留计数。 When the retain count drops to zero, the object is deallocated. 当保留计数降至零时,将取消分配对象。

In order for an object to continue to live, you need to have at least one strong reference to it. 为了使对象继续存在,您需要至少有一个强引用。 If you don't it will be deallocated. 如果不这样做,它将被取消分配。

A weak pointer is NOT an owning reference. 弱指针不是拥有引用。

If the only references to an object are weak references, it will be deallocated, possibly immediately. 如果对象的唯一引用是弱引用,则可能会立即释放它。 Weak references are special; 弱参考是特殊的; the compiler zeros them out when the object is deallocated. 当对象被释放时,编译器将它们清零。 This means that you won't crash if you try to send a message to an object saved in a weak variable. 这意味着如果您尝试向保存在弱变量中的对象发送消息,则不会崩溃。 If it was deallocated, the pointer is changed to nil and the message is simply ignored. 如果已取消分配,则指针将更改为nil,并且将忽略该消息。

EDIT 编辑

As pointed out by @vacawama, sending messages to a nil object is the Objective-C way of doing things. 正如@vacawama所指出的,向nil对象发送消息是Objective-C的做事方式。 (I've been working full-time for a client lately in Objective-C, so that tends to be my mindset lately. The question was about Swift however.) (我最近一直在为Objective-C的客户全职工作,所以这最近往往是我的心态。问题是关于Swift的。)

In Swift, you use optional chaining instead, and syntax like this: 在Swift中,您使用可选链接,语法如下:

object?.method().

With this Swift syntax, if object is nil, the method call is skipped. 使用此Swift语法,如果object为nil,则跳过方法调用。

VERY IMPORTANT: 很重要:

If you have 2 objects that each have weak references to each other, that's fine, but somewhere else in your program you need to have strong (owning) references to both objects or they will be deallocated. 如果你有2个对象,每个对象都有弱引用,那很好,但是你的程序中的其他地方你需要对两个对象都有强烈的(拥有)引用,否则它们将被释放。

ALSO VERY IMPORTANT: 非常重要:

If you have 2 objects that have strong references to each other, you've created a "retain cycle", and unless you nil one of those pointers at some time in the future, the objects will never be deallocated. 如果你有2个对象具有强引用,你就创建了一个“保留周期”,除非你将来某个时候忽略其中一个指针,否则这些对象永远不会被释放。 If you have 2 (or more) objects that have strong references to each other but you don't have any other references to those objects, you've caused a memory leak. 如果您有2个(或更多)对象具有强引用,但您没有对这些对象的任何其他引用,则会导致内存泄漏。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM