小例子:
class A {
var b: B!
deinit {
print("deinit of A")
}
}
class B {
var a: A!
deinit {
print("deinit of B")
}
}
do {
let a = A()
let b = B()
a.b = b
b.a = a
}
如果您运行此代码(可能在 Playground 中),它不会打印任何内容。这意味着deinit 从未调用过这两个对象,它们只是泄漏了。
但如果您将其中一个属性声明为weak:
class A {
weak var b: B!
deinit {
print("deinit of A")
}
}
然后deinit 将被调用,您将在控制台中看到消息。
编辑:添加带有闭包的示例
考虑这个例子:
class C {
var f: (Void -> Void)!
deinit {
print("deinit for C")
}
}
do {
let c = C()
c.f = {
print(c)
}
}
c 捕获 f,f 捕获 c。所以我们得到了内存泄漏。
要处理闭包中的泄漏,您有 2 个选项 - 声明捕获的对象是 weak 或 unowned。像这样:
do {
let c = C()
c.f = { [weak c] in
print(c)
}
}
基本上,如果在调用闭包时对象可能不存在并变为nil,您将使用weak;但如果您确定此时对象仍然存在,请改用unowned。
在我在闭包中将c 声明为weak 后,会打印“deinit for C”——这意味着所有内容都已成功解除分配。
这对开发者来说意味着什么?
几乎所有时候您都不必担心内存管理。它自动为您完成。对象只在你需要时存在,在你不需要时消失。但是有两种非常常见的情况,您需要小心并考虑内存。
- 委派。这是 Cocoa 中的常见模式,如果做错了,它可能会创建保留循环。除非您有充分的理由不这样做,否则请始终将您的代表声明为
weak。
- 关闭。闭包捕获周围范围内对象的引用并自动执行,恕不另行通知。实现关闭时,检查它是否会创建保留循环。如果是,请将问题变量声明为
weak 或unowned。
有关更多信息,我建议您阅读 Apple Swift 官方书籍,该书籍可在 iBooks 或 here 网站中找到。