Ban*_*tor 21 memory-leaks memory-management lazy-initialization automatic-ref-counting swift
使用惰性初始化器时,是否有可能保留周期?
在博客文章和许多其他地方[unowned self]
可见
class Person {
var name: String
lazy var personalizedGreeting: String = {
[unowned self] in
return "Hello, \(self.name)!"
}()
init(name: String) {
self.name = name
}
}
Run Code Online (Sandbox Code Playgroud)
我试过这个
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")
}
}
Run Code Online (Sandbox Code Playgroud)
像这样使用它
//...
let person = Person(name: "name")
print(person.personalizedGreeting)
//..
Run Code Online (Sandbox Code Playgroud)
并发现"人员deinit"被记录下来.
所以似乎没有保留周期.根据我的知识,当一个块捕获自我并且当该块被自己强烈保留时,存在保留周期.这种情况看起来类似于保留周期但实际上并非如此.
Nik*_*uhe 57
我试过这个[...]
lazy var personalizedGreeting: String = { return self.name }()
Run Code Online (Sandbox Code Playgroud)
似乎没有保留周期
正确.
其原因是,立即应用关闭{}()
被认为@noescape
.它不会保留捕获的内容self
.
供参考:Joe Groff的推文.
self
在实例化personalizedGreeting
.正如 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) // Hello Foo!
}
foo() // deinitialized!
Run Code Online (Sandbox Code Playgroud)
很明显,在这种情况下没有强引用循环的风险,因此不需要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() // ... nothing : strong reference cycle
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
即,f
infoo()
没有被取消self
初始化,并且给定这个强引用循环,我们可以得出结论,在惰性变量的实例化闭包中被强烈捕获dummy
。
我们还可以看到,如果我们从不实例化dummy
,我们从不创建强引用循环,这将支持最多一次延迟实例化闭包可以被视为运行时范围(很像一个从未达到的 if),要么a) 从未到达(未初始化)或 b) 到达、完全执行并“丢弃”(范围结束)。
/* Test 2: don't 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
return "dummy"
}()
deinit { print("deinitialized!") }
}
func foo() {
let p = Foo()
// Test 2: don't execute closure
// print(p.dummy)
}
foo() // deinitialized!
Run Code Online (Sandbox Code Playgroud)
有关强引用循环的其他阅读,请参见例如
归档时间: |
|
查看次数: |
5136 次 |
最近记录: |