swift - 为什么闭包捕获引用而函数没有捕获引用?另外,为什么闭包声明需要“lazy”关键字?
问题描述
我在 Xcode Playground 中使用以下代码对其进行了试验:
class X {
var a = 3
init(a: Int) {
self.a = a
}
deinit {
print("\(self.a) is deallocated.")
}
func returnX() -> Int {
return self.a
}
lazy var anotherReturnX: () -> Int = {
return self.a
}
}
var e: X? = X(a: 6)
print(e!.returnX())
e = nil // prints "6 is deallocated."
var f: X? = X(a: 7)
print(f!.anotherReturnX())
f = nil // prints nothing
从上面的代码中,我可以看到函数中没有捕获任何引用returnX()
,因此e
一旦我设置e
为nil
. 但是,在闭包中捕获了一个引用anotherReturnX()
,因此f
不会被释放。显然,这意味着闭包会捕获引用,而函数不会。
此外,当我第一次输入代码时,我没有lazy
在闭包声明之前包含关键字,因为我认为没有必要这样做。但是它会触发编译时错误。我推断,由于闭包只能在实例化后访问,所以它必须访问实例化的self
. 但是,既然我在这里声明的实际上是一个“匿名函数”,为什么self
在实例化期间闭包会访问呢?
经过一番思考,我发现了更多的矛盾。例如,我知道在调用闭包时会捕获引用。但是,在初始化期间X
,我只是将闭包分配给变量而不调用它,与其他实例属性的声明相同。因此,闭包在初始化期间不应该做任何事情,并且编译没有关键字的代码lazy
应该没问题。但是编译失败。我不确定我的理解出了什么问题。
我已经阅读了一些相关文章,例如强/弱引用、保留周期、惰性存储属性。然而,许多人解释“发生了什么”,并没有过多地谈论“为什么”。
所以除了标题中提出的问题之外,我还想澄清一下,是什么让函数和闭包彼此不同,从而导致上述情况发生?
更新:
闭包捕获引用而函数没有捕获引用这一事实在我身上进一步“强制”,因为根据 Xcode 编译器,我可以重写return self.a
为return a
in returnX()
,但我不能在anotherReturnX
. 因此,我想我不得不接受这一点,尽管函数和闭包是相似的,因为它们都是“一组功能”,但函数与闭包不同,它不捕获引用。如果我要深入了解这背后的原因,它可能会涉及到 Swift 本身的设计?
但是,我仍然不明白为什么lazy
闭包声明需要关键字。
解决方案
lazy var anotherReturnX: () -> Int = {
return self.a
}
这里的自我是一个坚强的自我。当一个对象强引用另一个对象时,ARC 不能解除分配,因此会创建一个保留循环。引用应该是弱的,以避免通过在块内创建弱自身来避免保留循环。
lazy var anotherReturnX: () -> Int = { [weak self] in
return self?.a
}
推荐阅读
- mysql - MySQL OUT 参数返回 NULL
- react-native - that.setState 不是函数。(expo react native)
- c# - 在 C# 中调用 post api 方法,而您必须设置代理服务器和端口
- angular - ExpressionChangedAfterItHasBeenCheckedError 微调器
- laravel - 使用 laravel 数据库通知时出现 500 错误。我正在为 ajax 使用 vue js
- c++ - meson-build:Windows 的 wxWidgets 资源文件未构建
- node.js - Mongoose 连接到 mongodb 的错误问题
- javascript - 如何从一个 wasm 文件加载两个或多个相同的 webassembly 模块?
- django - 从浏览器中删除应用程序 cookie 后调用重定向失败,提示 cors 错误
- java - Eclipse Vaadin 设计器 10+ html 模板