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

时间:2022-06-01 18:34:10

I'm making another attempt today to try to understand retain cycles and weak references in 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:

我今天又尝试去理解Swift中的保留周期和弱引用。通过阅读文档,我看到了下面的代码示例,其中一个引用变量被标记为弱,以防止保留周期:

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类中,我能把公寓变量改成弱的吗

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.

我在操场上测试了一下,效果还不错,但是有什么强烈的理由不这么做吗?在这种情况下,似乎没有自然的亲子关系。

3 个解决方案

#1


10  

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.

严格地说,公寓被拆毁时,人们并没有被杀害,公寓也没有被拆毁,因为在这种情况下,人们的死亡是如此的无力。通常最好考虑你想要的关系和所有权模型,然后决定如何实现它。

#2


5  

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. 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时,已创建的公寓由局部变量unit2B保留。当init完成时,公寓将被释放,因为不再有任何强引用来保存它,所以当调用person时程序会崩溃。公寓现在是零。

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.

如果您从类Person的弱var公寓中移除弱单元,那么这个示例不会崩溃,因为在init中创建的公寓由类property Person保留。

The other way to fix the example is to make unit2B be a property of class Test. Then the apartment would have a strong reference holding it so unit2B wouldn't be deallocated after init.

修正这个例子的另一种方法是使unit2B成为类测试的属性。这样公寓就会有很强的参考,所以在init之后,unit2B就不会被释放。

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.

如果从类Person的弱var公寓和类公寓的弱var租户中去除弱,那么示例不会崩溃,但由于两个对象相互之间具有强引用而创建的retain cycle不会释放Person和公寓。

#3


3  

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. When the retain count drops to zero, the object is deallocated.

核心概念是对象所有权。当您创建一个对象并在一个强变量中存储一个指向它的指针时,系统会增加该对象的保留计数。当变量超出范围或将nil存储到其中时,系统就会减少retain count。当保留计数下降到零时,对象被释放。

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. (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.)

@vacawama指出,向nil对象发送消息是Objective-C的做法。(我最近一直在Objective-C为一个客户全职工作,所以这是我最近的想法。但问题是关于斯威夫特的。

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.

使用这种快速语法,如果对象为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.

如果您有两个对象,每个对象都有弱引用,这很好,但是在您的程序中的其他地方,您需要对两个对象都有强(拥有)引用,否则它们将被释放。

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

如果有两个对象彼此之间具有强引用,那么就创建了一个“retain cycle”,除非在将来某个时候对其中的一个指针进行nil,否则这些对象永远不会被释放。如果有两个(或多个)对象彼此之间具有强引用,但没有任何其他对这些对象的引用,则会导致内存泄漏。

#1


10  

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.

严格地说,公寓被拆毁时,人们并没有被杀害,公寓也没有被拆毁,因为在这种情况下,人们的死亡是如此的无力。通常最好考虑你想要的关系和所有权模型,然后决定如何实现它。

#2


5  

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. 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时,已创建的公寓由局部变量unit2B保留。当init完成时,公寓将被释放,因为不再有任何强引用来保存它,所以当调用person时程序会崩溃。公寓现在是零。

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.

如果您从类Person的弱var公寓中移除弱单元,那么这个示例不会崩溃,因为在init中创建的公寓由类property Person保留。

The other way to fix the example is to make unit2B be a property of class Test. Then the apartment would have a strong reference holding it so unit2B wouldn't be deallocated after init.

修正这个例子的另一种方法是使unit2B成为类测试的属性。这样公寓就会有很强的参考,所以在init之后,unit2B就不会被释放。

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.

如果从类Person的弱var公寓和类公寓的弱var租户中去除弱,那么示例不会崩溃,但由于两个对象相互之间具有强引用而创建的retain cycle不会释放Person和公寓。

#3


3  

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. When the retain count drops to zero, the object is deallocated.

核心概念是对象所有权。当您创建一个对象并在一个强变量中存储一个指向它的指针时,系统会增加该对象的保留计数。当变量超出范围或将nil存储到其中时,系统就会减少retain count。当保留计数下降到零时,对象被释放。

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. (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.)

@vacawama指出,向nil对象发送消息是Objective-C的做法。(我最近一直在Objective-C为一个客户全职工作,所以这是我最近的想法。但问题是关于斯威夫特的。

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.

使用这种快速语法,如果对象为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.

如果您有两个对象,每个对象都有弱引用,这很好,但是在您的程序中的其他地方,您需要对两个对象都有强(拥有)引用,否则它们将被释放。

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

如果有两个对象彼此之间具有强引用,那么就创建了一个“retain cycle”,除非在将来某个时候对其中的一个指针进行nil,否则这些对象永远不会被释放。如果有两个(或多个)对象彼此之间具有强引用,但没有任何其他对这些对象的引用,则会导致内存泄漏。