Swift类在作用域末尾而不是在上次使用后取消初始化 [英] Swift class de-initialized at end of scope instead of after last usage

查看:73
本文介绍了Swift类在作用域末尾而不是在上次使用后取消初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经问过

I've asked this question asking about the guarantees of the lifetime of a reference in a local variable and was referred to this thread in which the exact semantics of the lifetime of references to class instances were discussed. AFAICT from that thread, a class instance may be de-initialized right after the last usage of a variable containing the last reference, even before other expressions in the same statement are evaluated. It is argued there that allowing this behavior is necessary to prevent creating unnecessary copies in copy-on-write data structures, which I can follow.

这引起了我的注意,到目前为止,我还从未见过Swift会以这种方式表现.每当我跟踪执行情况时,无论是在调试版本中还是在发布版本中,类实例都仅在离开完整作用域时才初始化.

This brought to my attention that up to now I've never seen Swift behave in that manner. Whenever I traced the execution, whether in a debug or release build, class instances were only initialised when leaving a complete scope.

在下面的main()示例中,我创建了一个类的实例,该类在控制台的生命周期内打印到控制台.请注意,在main()中的print()调用期间或之后不使用该实例:

In the following example in main(), I create an instance of a class which prints to the console during its lifecycle. Note that the instance is not used during or after the call to print() in main():

public final class Something {
    init() { print("Something.init()") }
    deinit { print("Something.deinit") }
}

func main() {
    let something = Something()
    print("in main()")
}

main()

在使用调试或发布配置构建和运行此示例时,我看到以下执行顺序,即在main()中调用print()期间实例保持活动状态:

When building and running this example with the debug or release configuration, I see the following order of execution, i.e. the instance is kept alive during the call to print() in main():

$ swift run -c release
Something.init()
in main()
Something.deinit

相反,我希望执行的顺序如下:

Instead, I would have expected the following order of execution:

$ swift run -c release
Something.init()
Something.deinit
in main()

我正在使用随Xcode 9.2分发的Swift编译器4.0.3:

I am using the Swift compiler 4.0.3 distributed with Xcode 9.2:

$ swift --version
Apple Swift version 4.0.3 (swiftlang-900.0.74.1 clang-900.0.39.2)
Target: x86_64-apple-macosx10.9

考虑到Swift编译器的开发人员试图通过减少ARC参考计数器(以及该取消初始化的类实例)来避免攻击,避免了写时复制的不必要复制,如何解释执行顺序数据结构?是否还有其他优化在起作用,因为在这种情况下,保持引用有效并不会导致不必要的工作(只是分配内存的时间超过了严格的要求)?

How can this order of execution be explained, considering that the developers of the Swift compiler are trying to be very agressive with decrementing ARC reference counters (and this de-initializing class instances) to avoid unnecessary copies in copy-on-write data structures? Is there some other optimization at play here because in this case keeping the reference alive does not actually lead to unnecessary work (just memory being allocated for longer than strictly necessary)?

我的问题不是要解决我遇到的特定问题,毕竟编译器正在创建的代码完全符合语言所允许的范围.我想很好地了解Swift编译器如何处理引用以及正在进行哪些优化,以便我可以评估哪些代码模式在紧急情况下(例如)导致良好的性能.当需要通过写时复制来复制大型数据结构时,或者当涉及大量需要递增和递减的引用时.

My question is not about solving a particular issue I have, after all the compiler is creating code that is well withing the bounds of what the language allows. I would like to get a good understanding of how the Swift compiler is treating references and what optimizations are at play so that I can gauge which code patterns lead to good performance in critical situations, e.g. when large data structures would need to be copied by copy-on-write or when a large number of references are involed which need to be incremented and decremented.

推荐答案

如果已将变量设为可选变量并将其设置为nil,则可以明确告知编译器要将引用释放到何处.

If you had made your variable an optional and set it to nil, you would have explicitly told the compiler where you want the reference to be released.

例如

func main() {
    var something:Something? = Something()
    something = nil
    print("in main()")
}

否则,让编译器自行决定执行顺序.以与可以决定无序执行任何代码行的方式相同,它还可以决定何时取消分配局部变量.

Otherwise you are letting the compiler decide execution order on its own. In the same way that it could decide to execute any line of code out of order, it can also decide when to deallocate local variables.

在您的示例中,"something"是一个非可选的局部变量,因此编译器很可能会决定将其分配到堆栈上.这将意味着变量的值(在堆栈上)将保留引用,直到函数返回.

In your example "something" is an non-optional local variable, so it is likely that the compiler will decide to allocate it on the stack. This will mean that the variable's value (on the stack) will hold the reference until the function returns.

这篇关于Swift类在作用域末尾而不是在上次使用后取消初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆