我今天再次尝试了解Swift中的保留周期和弱引用。通过documentation阅读,我看到了以下代码示例,其中一个引用变量被标记为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?
// ...
}
其中有两个相互引用的弱变量。
我在游乐场测试了它似乎工作正常,但有没有强有力的理由不这样做?在这种情况下,似乎没有自然的亲子关系。
答案 0 :(得分:14)
你可以做到这一点。唯一的副作用是你需要确保其他东西保留了人和公寓。在原始代码中,您只需要保留人员,公寓(与人相关)将保留给您。
严格地说,当公寓被拆除时人们没有被杀死,当人们死亡时公寓没有被拆除,因此这种情况下的弱参考是有道理的。通常最好考虑您想要的关系和所有权模型,然后决定如何实现这一目标。
答案 1 :(得分:5)
为了扩充已接受的答案,这里是一个演示行为的具体例子。
试试这是一个游乐场:
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()
在课程init
中Test
时,已创建的公寓由本地变量unit2B
保留。当init
完成后,公寓将被取消分配,因为不再有任何强引用,因此在调用test
时程序崩溃,因为person.apartment
现在是nil
如果您从weak
中的weak var apartment
移除class Person
,则此示例不会崩溃,因为init
中创建的公寓由person
保留由类属性person
保留的人。
修复示例的另一种方法是将unit2B
设为class Test
的属性。然后公寓会有一个强有力的参考,因此unit2B
之后init
将不会被取消分配。
如果您从weak
中的weak var apartment
和class Person
中的weak var tenant
移除class Apartment
,则示例不会崩溃,但{由于两个对象之间保持强引用所创建的保留周期,{1}}也不会释放Person
。
答案 2 :(得分:4)
您的问题没有提供足够的信息供我们回答。你需要退一步研究iOS内存管理。
核心概念是对象所有权。创建对象并在强变量中存储指向它的指针时,系统会增加该对象的保留计数。当变量超出范围或将nil存储到其中时,系统会减少保留计数。当保留计数降至零时,将取消分配对象。
为了让对象继续存在,您需要至少有一个强引用。如果不这样做,它将被解除分配。
弱指针不是拥有引用。
如果对象的唯一引用是弱引用,则可能会立即释放它。弱参考是特殊的;当对象被释放时,编译器将它们清零。这意味着如果您尝试向保存在弱变量中的对象发送消息,则不会崩溃。如果它被解除分配,指针将变为nil,并且消息将被忽略。
正如@vacawama所指出的,向nil对象发送消息是Objective-C的做事方式。 (我最近一直在Objective-C为一位客户全职工作,所以最近我的心态一直是这样。问题是关于Swift的。)
在Swift中,您使用可选链接,语法如下:
object?.method().
使用此Swift语法,如果object为nil,则跳过方法调用。
如果你有2个对象,每个对象都有弱引用,那很好,但是你的程序中的其他地方你需要对这两个对象有强烈的(拥有)引用,否则它们将被释放。
如果你有2个对象具有强引用,你就创建了一个“保留周期”,除非你将来某个时候忽略其中一个指针,否则这些对象将永远不会被释放。如果您有2个(或更多)对象具有强引用,但您没有对这些对象的任何其他引用,则会导致内存泄漏。