使用延迟初始化器时,是否有可能保留周期?
在blog post和许多其他地方[unowned self]
可见
class Person {
var name: String
lazy var personalizedGreeting: String = {
[unowned self] in
return "Hello, \(self.name)!"
}()
init(name: String) {
self.name = name
}
}
我试过这个
class Person {
var name: String
lazy var personalizedGreeting: String = {
//[unowned self] in
return "Hello, \(self.name)!"
}()
init(name: String) {
print("person init")
self.name = name
}
deinit {
print("person deinit")
}
}
像这样使用它
//...
let person = Person(name: "name")
print(person.personalizedGreeting)
//..
发现" deinit"被记录了。
所以似乎没有保留周期。 根据我的知识,当一个块捕获自我并且当该块被自己强烈保留时,存在保留周期。这种情况看起来类似于保留周期,但实际上并非如此。
答案 0 :(得分:54)
我试过这个[...]
lazy var personalizedGreeting: String = { return self.name }()
似乎没有保留周期
正确。
原因是立即应用的关闭{}()
被视为@noescape
。它不会保留捕获的self
。
供参考:Joe Groff's tweet。
答案 1 :(得分:4)
self
实例化后没有引用personalizedGreeting
。正如MartinR在他的评论中写道,当您删除捕获列表时,可以通过记录Person
对象是否已被去除来轻松测试您的假设。
E.g。
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) // Hello Foo!
}
foo() // deinitialized!
很明显,在这种情况下不存在强引用周期的风险,因此,在延迟闭包中不需要unowned self
的捕获列表。这样做的原因是延迟闭包只执行一次,并且只使用闭包的返回值(懒惰地)实例化personalizedGreeting
,而对self
的引用在这种情况下,不会超过闭包的执行。
但是,如果我们要在Person
的类属性中存储类似的闭包,我们会创建一个强引用循环,因为self
的属性会将强引用保留回{{ 1}}。 E.g:
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() // ... nothing : strong reference cycle
捕获为self
(或weak
),默认情况下在我们考虑下面的例子时,我们意识到这个假设是错误的。
unowned
即/* Test 1: execute lazy instantiation closure */
class Bar {
var foo: Foo? = nil
}
class Foo {
let bar = Bar()
lazy var dummy: String = {
_ in
print("executed")
self.bar.foo = self
/* if self is captured as strong, the deinit
will never be reached, given that this
closure is executed */
return "dummy"
}()
deinit { print("deinitialized!") }
}
func foo() {
let f = Foo()
// Test 1: execute closure
print(f.dummy) // executed, dummy
}
foo() // ... nothing: strong reference cycle
中的f
未被取消初始化,并且考虑到这个强大的参考周期,我们可以得出结论:foo()
在惰性变量{{的实例化闭包中被强烈捕获1}}。
我们还可以看到,如果我们从不实例化self
,我们永远不会创建强引用循环,这将支持最多一次的实例化闭包可以被视为运行时范围(很像a)从未达到if)a)从未达到(未初始化)或b)达到,完全执行和“扔掉”(范围结束)。
dummy
有关强参考循环的其他读数,请参见例如