繁体   English   中英

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

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

我今天再次尝试尝试理解Swift中的保留周期和弱引用。 通过阅读文档 ,我看到了以下代码示例,其中一个引用变量被标记为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"

让变量变弱都有问题吗? 也就是说,在Person类中,我可以将apartment变量更改为弱,以便我拥有

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

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

其中有两个相互引用的弱变量。

我在游乐场测试了它似乎工作正常,但有没有强烈的理由不这样做? 在这种情况下似乎没有自然的亲子关系。

你可以做到这一点。 唯一的副作用是你需要确保其他东西保留了人和公寓。 在原始代码中,您只需要保留人员,公寓(与人相关)将保留给您。

严格来说,当公寓被拆除时人们没有被杀死,当人们死亡时公寓没有被拆除,因此这种情况下的弱参考是有道理的。 通常最好考虑您想要的关系和所有权模型,然后决定如何实现这一目标。

为了增加接受的答案,这里是一个演示行为的具体例子。

试试这是一个游乐场:

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

在类Testinit时,已创建的公寓由本地变量unit2B保留。 init完成时,公寓将被释放,因为不再有任何强引用,因此当调用test时程序崩溃,因为person.apartment现在nil

如果从class Person weak var apartment删除weak ,则此示例不会崩溃,因为在init创建的单元由类属性person保留的person保留。

修复示例的另一种方法是使unit2B成为class Test的属性。 然后公寓将有一个强大的参考保持它,所以unit2B将不会在init后解除分配。

如果从class Person weak var apartmentclass Apartment weak var tenant中删除weak ,则该示例不会崩溃,但是由于两个保持强大的对象所创建的保留周期, PersonApartment都不会被释放相互引用。

您的问题没有提供足够的信息供我们回答。 您需要退一步研究iOS内存管理。

核心概念是对象所有权。 创建对象并在强变量中存储指向它的指针时,系统会增加该对象的保留计数。 当变量超出范围或将nil存储到其中时,系统会减少保留计数。 当保留计数降至零时,将取消分配对象。

为了使对象继续存在,您需要至少有一个强引用。 如果不这样做,它将被取消分配。

弱指针不是拥有引用。

如果对象的唯一引用是弱引用,则可能会立即释放它。 弱参考是特殊的; 当对象被释放时,编译器将它们清零。 这意味着如果您尝试向保存在弱变量中的对象发送消息,则不会崩溃。 如果已取消分配,则指针将更改为nil,并且将忽略该消息。

编辑

正如@vacawama所指出的,向nil对象发送消息是Objective-C的做事方式。 (我最近一直在为Objective-C的客户全职工作,所以这最近往往是我的心态。问题是关于Swift的。)

在Swift中,您使用可选链接,语法如下:

object?.method().

使用此Swift语法,如果object为nil,则跳过方法调用。

很重要:

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

非常重要:

如果你有2个对象具有强引用,你就创建了一个“保留周期”,除非你将来某个时候忽略其中一个指针,否则这些对象永远不会被释放。 如果您有2个(或更多)对象具有强引用,但您没有对这些对象的任何其他引用,则会导致内存泄漏。

暂无
暂无

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

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