在这种情况下,您不需要捕获列表,因为在实例化 personalizedGreeting
之后没有涉及到 self
的引用。
正如MartinR在他的评论中写道,您可以通过记录在删除捕获列表时是否已经初始化 Person
对象来轻松测试您的假设。
例如:
class Person {
var name: String
lazy var personalizedGreeting: String = {
_ in
return "Hello, \(self.name)!"
}()
init(name: String) {
self.name = name
}
deinit { print("deinitialized!") }
}
func foo() {
let p = Person(name: "Foo")
print(p.personalizedGreeting)
}
foo()
在这种情况下,明显不存在强引用循环的风险,因此在惰性闭包中不需要使用捕获列表
unowned self
。原因是惰性闭包仅执行一次,并且仅使用闭包的返回值来(懒惰地)实例化
personalizedGreeting
,而
self
的引用不会在这种情况下超出闭包的执行范围。
但是,如果我们将类似的闭包存储在
Person
的类属性中,则会创建强引用循环,因为
self
的属性将保留对
self
的强引用。例如:
class Person {
var name: String
var personalizedGreeting: (() -> String)?
init(name: String) {
self.name = name
personalizedGreeting = {
() -> String in return "Hello, \(self.name)!"
}
}
deinit { print("deinitialized!") }
}
func foo() {
let p = Person(name: "Foo")
}
foo()
假设:懒加载闭包默认会将self
作为weak
(或unowned
)进行捕获
通过考虑以下示例,我们意识到这个假设是错误的。
class Bar {
var foo: Foo? = nil
}
class Foo {
let bar = Bar()
lazy var dummy: String = {
_ in
print("executed")
self.bar.foo = self
return "dummy"
}()
deinit { print("deinitialized!") }
}
func foo() {
let f = Foo()
print(f.dummy)
}
foo()
即在
foo()
中的
f
未被取消初始化,考虑到这种强引用循环,我们可以得出结论:实例闭包中的惰性变量
dummy
捕获了
self
。
另外,如果我们永远不实例化
dummy
,就不会创建强引用循环,这表明最多一次的惰性实例化闭包可以看作是运行时作用域(类似于永远不会到达的if语句),即a)永远不会到达(未初始化),或b)到达,完全执行并“抛弃”(作用域结束)。
class Bar {
var foo: Foo? = nil
}
class Foo {
let bar = Bar()
lazy var dummy: String = {
_ in
print("executed")
self.bar.foo = self
return "dummy"
}()
deinit { print("deinitialized!") }
}
func foo() {
let p = Foo()
}
foo()
关于强引用循环的更多阅读材料,例如:
deinit
方法并检查当你期望对象被释放时是否被调用。或者使用 Xcode/Instruments 中的内存调试工具。 - Martin Rlazy
初始化程序无关。 - holex