这个问题让我抓狂了好几天,但还没有人能让我清楚地了解到底发生了什么。这是代码的第一个片段
class Animal {
var name = "Fischer"
var command: () -> Void = { }
deinit {
print(#function, #line)
}
}
do {
var pet: Animal? = Animal()
pet?.command = { print(pet?.name ?? "Bobby") }
}
这段代码会导致内存泄漏,因为
class Animal {
var name = "Fischer"
var command: () -> Void = { }
deinit {
print(#function, #line)
}
}
do {
var pet: Animal? = Animal()
pet?.command = { print(pet?.name ?? "Bobby") }
pet = nil
}
繁荣! deinit 被调用,意味着对象被释放,但是如何释放呢?为什么对象被释放?如果我们要删除完全相同的引用,那么该引用是在第一个片段中的“do”范围末尾删除的吗?我误解了什么吗?我很长时间都在努力理解这一点,但我仍然不明白它是如何工作的。
如果关闭,你的推理是正确的
{ [pet] in print(pet?.name ?? "Bobby") }
上面的闭包确实对
Animal
对象有强引用,而Animal
对象对闭包也有强引用,从而创建了一个retain循环。
但是,您的闭包捕获了 变量
pet
。它对变量 pet
引用的任何对象都有强引用。如果 pet
设置为 nil,则闭包不会对 pet
之前引用的对象有强引用。
比较:
do {
var pet: Animal? = Animal()
// this closure captures the variable 'pet', so 'pet = nil' later will affect what this closure does
pet?.command = { print(pet?.name ?? "Bobby") }
let pet2 = pet
// the 'pet' in the closure now becomes nil
pet = nil
pet2?.command() // this prints "Bobby"
}
do {
var pet: Animal? = Animal()
// this closure captures the object referred to by 'pet' at this moment
pet?.command = { [pet] in print(pet?.name ?? "Bobby") }
let pet2 = pet
// this does not change the object captured by the closure
pet = nil
pet2?.command() // this prints "Fischer"
}
即使在
do
块完成之后,变量 pet
也不会被“删除”——它被保存在闭包中。由于它是被闭包捕获的,因此它被放在堆上,而不是留在堆栈上。