【问题标题】:deinit not called in specific case在特定情况下不调用 deinit
【发布时间】:2018-08-03 16:27:36
【问题描述】:

我有以下测试用例:我希望在程序终止时调用deinit,但它永远不会。我是 Swift 的新手,但不认为这是预期的行为。 (这不是在操场上)

class Test
{
   init() {
      print( "init" )
   }

   deinit {
      print( "deinit" )
   }
}

print("Starting app")

var test = Test()

print( "Ending App" )

输出是:

Starting app
init
Ending App
Program ended with exit code: 0

如果我将代码放在一个函数中,然后调用该函数,我会得到预期的结果

Starting app
init
Ending App
deinit
Program ended with exit code: 0

不应该在程序终止时调用对象的 deinit 吗?

【问题讨论】:

  • 因为它是在顶层定义的,test 在技术上是一个全局变量,因此它实际上永远不会超出范围。
  • 如果将你的“主代码”放在do { ... } 块中,那么 deinit 将被调用。
  • @Hamish 这没有任何意义,它在程序终止时超出了范围! (或者至少在逻辑上应该)。很容易解决,但它真的没有意义。
  • @GregorBrandt 你可以这么说,但是去初始化程序不能在 after 程序终止后运行 :) 虽然话虽如此,但顶级 main.swift 范围已经很特别了(全局变量没有惰性初始化器,您可以抛出未捕获的错误等),因此取消初始化器在程序终止之前运行可能是合理的;随时file a bug if you want

标签: swift automatic-ref-counting


【解决方案1】:

There 是一个类似的问题。

在 Apple 中没有明确的解释 deinit 是如何工作的。只是它在类释放时由 ARC 调用。我认为当应用程序终止时,与类的常规运行时取消初始化相比,取消初始化的机制不同。或者也许主窗口保留你的课程?无论如何,为了在应用程序终止时执行代码,您应该使用应用程序委托(如applicationWillTerminate)。

【讨论】:

    【解决方案2】:

    我希望在程序终止时调用 deinit

    你不应该期望那样。程序终止时存在的对象通常不会被释放。内存清理留给操作系统(释放程序的所有内存)。这是 Cocoa 中长期存在的优化程序,用于加速程序终止。

    deinit 仅用于释放资源(例如释放不在 ARC 下的内存)。在 ObjC 或 Swift 中没有等效的 C++ 析构函数。 (C++ 和 Objective-C++ 对象在程序终止期间被销毁,因为这是规范所要求的。)

    【讨论】:

    • 终止是一回事,但是方法返回或抛出呢? deinit 是在最后一个 obj-ref 超出范围时立即调用,还是我应该一次又一次地使用 defer 来代替,哪里需要这种行为?
    • @Top-Master deinit 如果对象被提前释放(非常常见并且生命周期规则允许),则可能会在变量超出范围之前很久调用。 deinit 也可能在最后一个引用超出范围后被显着调用,如果它上面有额外的自动释放。 (这在纯 Swift 类型中不太常见,但在处理涉及 ObjC 的事情时很常见,Swift 经常在幕后做。)如果你想在退出范围时执行某些事情,这就是 defer 的用途,而不是deinit
    • 如果MySwiftClass继承了我的Objective-C++类;当最后一个引用超出范围(通过方法返回或抛出)时,Obj-C++ 类的析构函数会立即被调用吗?如果是,我应该实现func dispose() 并在销毁期间从 Obj-C++ 类中调用它吗? (我的意思是,我认为继承是不够的,对吗?)
    • 除非用objc_precise_lifetime 注释,否则该对象仍不承诺在其整个范围内存在(您不能在 Swift 中添加它;它仅在 C 中可用)。在 Swift 中,您可以使用 withExtendedLifetime 来实现它,以确保对象不会比预期的更早被销毁。 C++ 析构函数的唯一变化是它将在 exit() 期间被调用。如果对象已被自动释放,它的生命周期可能仍会延长。您所描述的工具是defer,而不是各种释放方法中的任何一种。
    • 如果您尝试在 Swift 中重新创建 RAII,那么这不是 Swift 中的模式。 Swift 最常为此使用闭包(例如,withUnsafeBytes 使 Data 的字节存储在闭包中可用)。否则它使用defer。 Swift 提供了像withExtendedLifetime 这样的工具,这样你就可以处理确实依赖于精确生命周期的事情(主要是C++事情)。但是 Swift 更喜欢使用闭包的显式作用域,而不是像 RAII 通常使用的隐式作用域。 (如果你有一个具体的问题要解决,你应该打开一个新问题。)
    【解决方案3】:

    如果我将代码放在一个函数中,然后调用我得到的函数 预期结果

    是的,这是可行的,因为test 变量的生命周期是由方法的范围定义的。当方法的作用域结束时(即方法从Stack中删除),所有局部变量的生命都将结束,除非有其他对这些变量的强引用。

    不应在程序终止时调用对象的 deinit

    在 iOS 中,无法像 Android 那样以编程方式优雅地关闭应用程序。可以关闭应用程序的可能方式是,

    1. 从最近的应用列表中向上滑动(终止)
    2. 或故意崩溃。
    3. 或者让操作系统因内存不足而终止您的应用。

    你的应用程序的所有内存将在它终止时被操作系统清除,所以deinit 不会被调用,我们也不能指望它。你可以注意到termination这个词,它解释了程序没有以适当的方式结束的事实,所以我们不能指望操作系统做最后的荣誉。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-26
      • 1970-01-01
      • 1970-01-01
      • 2018-03-05
      相关资源
      最近更新 更多