Swift 中相互引用的两个弱变量?
Two weak variables referencing each other in Swift?
我今天再次尝试理解 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
class 中,我可以将 apartment
变量更改为 weak 以便我有
class Person {
// ...
weak var apartment: Apartment? // added 'weak'
// ...
}
class Apartment {
// ...
weak var tenant: Person?
// ...
}
其中有两个相互引用的弱变量。
我在 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()
在classinit
时Test
,已经创建的公寓由局部变量unit2B
保留。当 init
完成时,公寓将被释放,因为不再有任何强引用持有它,所以当调用 test
时程序崩溃,因为 person.apartment
现在是 nil
.
如果您从 class Person
中的 weak var apartment
中删除 weak
,则此示例不会崩溃,因为 init
中创建的公寓由 person
谁被class属性person
.
保留
另一种 修复 示例的方法是使 unit2B
成为 class Test
的 属性。然后公寓将有一个强大的引用持有它所以 unit2B
不会在 init
.
之后被释放
如果从 class Person
中的 weak var apartment
和 class Apartment
中的 weak var tenant
中删除 weak
,则示例不会崩溃,但两者都不会Person
和 Apartment
都将被释放,因为两个对象相互持有强引用而创建了保留循环。
您的问题没有提供足够的信息让我们回答。你需要退一步研究iOS内存管理。
核心概念是对象所有权。当您创建一个对象并将指向它的指针存储在强变量中时,系统会增加该对象的保留计数。当变量超出范围或您将 nil 存储到其中时,系统会减少保留计数。当保留计数降为零时,对象将被释放。
为了让一个对象继续存在,你需要至少有一个对它的强引用。如果你不这样做,它将被释放。
弱指针不是拥有引用。
如果对一个对象的唯一引用是弱引用,它将被释放,可能是立即释放。弱引用是特殊的;当对象被释放时,编译器将它们清零。这意味着如果您尝试向保存在弱变量中的对象发送消息,您将不会崩溃。如果它被释放,指针将变为 nil,消息将被忽略。
编辑
正如@vacawama 所指出的,向 nil 对象发送消息是 Objective-C 做事的方式。 (我最近在 Objective-C 为一位客户工作 full-time,所以这往往是我最近的心态。但是问题是关于 Swift。)
在 Swift 中,您使用可选链接代替,语法如下:
object?.method().
使用此 Swift 语法,如果对象为 nil,则跳过方法调用。
非常重要:
如果您有 2 个对象,每个对象都对彼此有弱引用,那很好,但是在您程序的其他地方,您需要对这两个对象有强(拥有)引用,否则它们将被释放。
同样非常重要:
如果你有 2 个对象,它们彼此有强引用,你就创建了一个“保留循环”,除非你在将来的某个时间将其中一个指针置零,否则这些对象将永远不会被释放。如果您有 2 个(或更多)对象彼此具有强引用,但您没有对这些对象的任何其他引用,则您已导致内存泄漏。
我今天再次尝试理解 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
class 中,我可以将 apartment
变量更改为 weak 以便我有
class Person {
// ...
weak var apartment: Apartment? // added 'weak'
// ...
}
class Apartment {
// ...
weak var tenant: Person?
// ...
}
其中有两个相互引用的弱变量。
我在 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()
在classinit
时Test
,已经创建的公寓由局部变量unit2B
保留。当 init
完成时,公寓将被释放,因为不再有任何强引用持有它,所以当调用 test
时程序崩溃,因为 person.apartment
现在是 nil
.
如果您从 class Person
中的 weak var apartment
中删除 weak
,则此示例不会崩溃,因为 init
中创建的公寓由 person
谁被class属性person
.
另一种 修复 示例的方法是使 unit2B
成为 class Test
的 属性。然后公寓将有一个强大的引用持有它所以 unit2B
不会在 init
.
如果从 class Person
中的 weak var apartment
和 class Apartment
中的 weak var tenant
中删除 weak
,则示例不会崩溃,但两者都不会Person
和 Apartment
都将被释放,因为两个对象相互持有强引用而创建了保留循环。
您的问题没有提供足够的信息让我们回答。你需要退一步研究iOS内存管理。
核心概念是对象所有权。当您创建一个对象并将指向它的指针存储在强变量中时,系统会增加该对象的保留计数。当变量超出范围或您将 nil 存储到其中时,系统会减少保留计数。当保留计数降为零时,对象将被释放。
为了让一个对象继续存在,你需要至少有一个对它的强引用。如果你不这样做,它将被释放。
弱指针不是拥有引用。
如果对一个对象的唯一引用是弱引用,它将被释放,可能是立即释放。弱引用是特殊的;当对象被释放时,编译器将它们清零。这意味着如果您尝试向保存在弱变量中的对象发送消息,您将不会崩溃。如果它被释放,指针将变为 nil,消息将被忽略。
编辑
正如@vacawama 所指出的,向 nil 对象发送消息是 Objective-C 做事的方式。 (我最近在 Objective-C 为一位客户工作 full-time,所以这往往是我最近的心态。但是问题是关于 Swift。)
在 Swift 中,您使用可选链接代替,语法如下:
object?.method().
使用此 Swift 语法,如果对象为 nil,则跳过方法调用。
非常重要:
如果您有 2 个对象,每个对象都对彼此有弱引用,那很好,但是在您程序的其他地方,您需要对这两个对象有强(拥有)引用,否则它们将被释放。
同样非常重要:
如果你有 2 个对象,它们彼此有强引用,你就创建了一个“保留循环”,除非你在将来的某个时间将其中一个指针置零,否则这些对象将永远不会被释放。如果您有 2 个(或更多)对象彼此具有强引用,但您没有对这些对象的任何其他引用,则您已导致内存泄漏。