Swift 和 RAII:项目和 playground 之间的 deinit 行为不一致
Swift and RAII: deinit behavior inconsistent between project and playground
让我先声明我已经通读了 this other question and the documentation for ARC 中的所有内容。
我正在尝试使用 RAII 习惯用法来处理问题,但 运行 遇到一些不一致的地方,导致它无法正常工作。
这是一个证明不一致的例子:
class TestClass {
init() {
print("init")
}
deinit {
print("deinit")
}
}
func test() {
TestClass()
defer { print("defer") }
print("end of scope")
}
test()
根据我的 C++ 经验,我希望 TestClass
实例的 deinit 发生在 test
函数范围的末尾,这就是我想要的行为做。
同时我意识到我正在初始化 TestClass
的一个实例,但没有将它存储在一个变量中,所以没有对它的强引用,因此它是有意义的立即解除分配。
所以问题和问题就出现了,因为这种行为在项目和游乐场之间是不一致的。
在操场上,上面的代码产生以下输出:
init
end of scope
defer
deinit // deinit happens at end of scope
after scope
但是在一个项目中,完全相同的代码会产生不同的结果:
init
deinit // deinit happens immediately
end of scope
defer
after scope
所以
- 为什么项目和 playground 之间的行为不同?
- 有没有办法control/guarantee项目中将使用哪种行为?如果可能的话,我想要操场行为。
为了在边栏和结果预览中显示对象,Playgrounds 对对象进行了各种引用。
您应该将 playground 的强引用视为完全不确定的。对于任何实际的生命周期调试,您需要 运行 将您的代码放在独立的程序、库等中。
Playgrounds 的编译方式不同——与常规编译不同,编译器通过 inserting various calls to instrumentation 转换您的代码。例如,这使您可以查看表达式的计算结果。然而,它可能会带来不幸的副作用,即改变程序的行为方式。
但值得注意的是,与 C 和 C++ 等语言不同,Swift 不保证局部变量(或者在您的情况下,未使用的表达式的值)在范围结束之前保持有效他们被定义。优化器可以自由地提前取消初始化它们。
如果你想在你的例子中保证 SomeClass
的生命周期,你可以使用 withExtendedLifetime
:
func test() {
withExtendedLifetime(TestClass()) {
defer { print("defer") }
print("end of scope")
}
}
让我先声明我已经通读了 this other question and the documentation for ARC 中的所有内容。
我正在尝试使用 RAII 习惯用法来处理问题,但 运行 遇到一些不一致的地方,导致它无法正常工作。
这是一个证明不一致的例子:
class TestClass {
init() {
print("init")
}
deinit {
print("deinit")
}
}
func test() {
TestClass()
defer { print("defer") }
print("end of scope")
}
test()
根据我的 C++ 经验,我希望 TestClass
实例的 deinit 发生在 test
函数范围的末尾,这就是我想要的行为做。
同时我意识到我正在初始化 TestClass
的一个实例,但没有将它存储在一个变量中,所以没有对它的强引用,因此它是有意义的立即解除分配。
所以问题和问题就出现了,因为这种行为在项目和游乐场之间是不一致的。
在操场上,上面的代码产生以下输出:
init
end of scope
defer
deinit // deinit happens at end of scope
after scope
但是在一个项目中,完全相同的代码会产生不同的结果:
init
deinit // deinit happens immediately
end of scope
defer
after scope
所以
- 为什么项目和 playground 之间的行为不同?
- 有没有办法control/guarantee项目中将使用哪种行为?如果可能的话,我想要操场行为。
为了在边栏和结果预览中显示对象,Playgrounds 对对象进行了各种引用。
您应该将 playground 的强引用视为完全不确定的。对于任何实际的生命周期调试,您需要 运行 将您的代码放在独立的程序、库等中。
Playgrounds 的编译方式不同——与常规编译不同,编译器通过 inserting various calls to instrumentation 转换您的代码。例如,这使您可以查看表达式的计算结果。然而,它可能会带来不幸的副作用,即改变程序的行为方式。
但值得注意的是,与 C 和 C++ 等语言不同,Swift 不保证局部变量(或者在您的情况下,未使用的表达式的值)在范围结束之前保持有效他们被定义。优化器可以自由地提前取消初始化它们。
如果你想在你的例子中保证 SomeClass
的生命周期,你可以使用 withExtendedLifetime
:
func test() {
withExtendedLifetime(TestClass()) {
defer { print("defer") }
print("end of scope")
}
}