不同的闭包在 swift 中给出不同的保留循环结果
Different closures giving different results for retain cycles in swift
我正在阅读 Apple 的 Swift 编程语言指南。在关于闭包的强引用循环的部分,我尝试了一种不同类型的闭包,但它没有给出预期的输出。
class HTMLElement {
let name: String
let text: String?
lazy var asHTML : String = {
//[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}()
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
println("\(name) is being deinitialized")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
println(paragraph!.asHTML)
paragraph = nil
这段代码的输出是:
<p>hello, world</p>
p is being deinitialized
即使没有 [unowned self] 也会打印 "p is being deinitialised"
指南中的代码是:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
println("\(name) is being deinitialized")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
println(paragraph!.asHTML)
paragraph = nil
仅当添加 [unowned self] 语句时才打印反初始化消息。
这两种闭包有什么区别?
在 Swift 书中的示例中,asHTML
是一个 闭包 :
lazy var asHTML : () -> String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
self
持有对闭包的强引用(通过 asHTML
属性), 和
闭包持有对 self
的强引用(除非你添加
[unowned self]
).
在您的示例中,asHTML
是 属性 类型 String
:
lazy var asHTML : String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}()
右侧被评估一次,当惰性 属性 被访问时
第一次。创建(捕获 self
)并评估闭包对象。之后,闭包对象被销毁(因为没有
存在对它的强烈引用)。特别是,捕获的参考
to self
已发布:不再关闭,没有保留周期。
好问题!为了探索它,我们应该将测试用例简化到演示它所需的最低限度:
// First case, a lazy string
class LazyVar {
let name = "LazyVar"
lazy var lazyName : String = { return self.name }()
deinit {
println("\(name) is being deinitialized")
}
}
var lazyVar: LazyVar? = LazyVar()
println(lazyVar?.lazyName)
lazyVar = nil // Calls deinit
// Second case, a lazy ()->String
class LazyFunc {
let name = "LazyFunc"
lazy var lazyFunc: () -> String = {
// [unowned self] in // <-- This would break the loop
return self.name
}
deinit {
println("\(name) is being deinitialized")
}
}
var lazyFunc: LazyFunc? = LazyFunc()
println(lazyFunc?.lazyFunc())
lazyFunc = nil // Doesn't call deinit
第一种情况,没有永久保留循环。当您访问 lazyName
时,会评估一个闭包 { return self.name }
。该闭包捕获 self
,计算字符串,然后 returns 字符串。该字符串分配给 属性。我们现在完成了闭包,所以它被释放了,它释放了 self
。请注意,这里有一个简短的保留循环,但没关系。它最终会被打破,这才是最重要的。
在第二种情况下,有一个永久保留循环。当您访问 lazyFunc()
时,它会调用一个创建 新闭包 的闭包。那个新的闭包是 {return self.name}
,并且分配给了 属性。那个闭包保留 self
,一个永不中断的保留循环也是如此。如标记的那样将 [unowned self] in
添加到 lazyFunc
将为您打破循环。
我正在阅读 Apple 的 Swift 编程语言指南。在关于闭包的强引用循环的部分,我尝试了一种不同类型的闭包,但它没有给出预期的输出。
class HTMLElement {
let name: String
let text: String?
lazy var asHTML : String = {
//[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}()
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
println("\(name) is being deinitialized")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
println(paragraph!.asHTML)
paragraph = nil
这段代码的输出是:
<p>hello, world</p>
p is being deinitialized
即使没有 [unowned self] 也会打印 "p is being deinitialised"
指南中的代码是:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
println("\(name) is being deinitialized")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
println(paragraph!.asHTML)
paragraph = nil
仅当添加 [unowned self] 语句时才打印反初始化消息。
这两种闭包有什么区别?
在 Swift 书中的示例中,asHTML
是一个 闭包 :
lazy var asHTML : () -> String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
self
持有对闭包的强引用(通过 asHTML
属性), 和
闭包持有对 self
的强引用(除非你添加
[unowned self]
).
在您的示例中,asHTML
是 属性 类型 String
:
lazy var asHTML : String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}()
右侧被评估一次,当惰性 属性 被访问时
第一次。创建(捕获 self
)并评估闭包对象。之后,闭包对象被销毁(因为没有
存在对它的强烈引用)。特别是,捕获的参考
to self
已发布:不再关闭,没有保留周期。
好问题!为了探索它,我们应该将测试用例简化到演示它所需的最低限度:
// First case, a lazy string
class LazyVar {
let name = "LazyVar"
lazy var lazyName : String = { return self.name }()
deinit {
println("\(name) is being deinitialized")
}
}
var lazyVar: LazyVar? = LazyVar()
println(lazyVar?.lazyName)
lazyVar = nil // Calls deinit
// Second case, a lazy ()->String
class LazyFunc {
let name = "LazyFunc"
lazy var lazyFunc: () -> String = {
// [unowned self] in // <-- This would break the loop
return self.name
}
deinit {
println("\(name) is being deinitialized")
}
}
var lazyFunc: LazyFunc? = LazyFunc()
println(lazyFunc?.lazyFunc())
lazyFunc = nil // Doesn't call deinit
第一种情况,没有永久保留循环。当您访问 lazyName
时,会评估一个闭包 { return self.name }
。该闭包捕获 self
,计算字符串,然后 returns 字符串。该字符串分配给 属性。我们现在完成了闭包,所以它被释放了,它释放了 self
。请注意,这里有一个简短的保留循环,但没关系。它最终会被打破,这才是最重要的。
在第二种情况下,有一个永久保留循环。当您访问 lazyFunc()
时,它会调用一个创建 新闭包 的闭包。那个新的闭包是 {return self.name}
,并且分配给了 属性。那个闭包保留 self
,一个永不中断的保留循环也是如此。如标记的那样将 [unowned self] in
添加到 lazyFunc
将为您打破循环。