没有在 UIViewController 上调用 Deinit,但 Dealloc 是

Posted

技术标签:

【中文标题】没有在 UIViewController 上调用 Deinit,但 Dealloc 是【英文标题】:Deinit not called on a UIViewController, but Dealloc is 【发布时间】:2016-01-11 18:19:18 【问题描述】:

它seems like the Swift equivalent of dealloc is deinit。但是,当您尝试在 UIViewController 上定义该方法时,它的行为与您预期的不同...

设置

    在 Swift 或 Objective-C 中使用 Xcode 7.0 创建一个新的 Single View 项目。 在使用情节提要创建的视图控制器上添加一个“关闭”按钮(我将此视图控制器称为 VC2;它的类是 ViewController)。 添加一个新的视图控制器并将其设置为初始视图控制器(VC1,类为 nil)。 在 VC1 中添加一个“present”按钮,并在 VC2 中添加“Present Modally”segue。 在 VC2 的代码中,在 deinit (Swift) 或 dealloc (Objective-C) 中放置一个断点。

    在 VC2 中,让“关闭”按钮的动作如下:

    // Swift:
    presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
    
    // Objective-C:
    [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
    
    运行应用程序并点按两个按钮以首先显示 VC2,然后将其关闭。

注意在 Objective-C 中,dealloc 断点是如何命中的

另一方面,在 Swift 中,deinit 断点永远不会被命中

为什么从未调用过deinit这是一个错误还是设计使然?

如果这是设计使然,当不再需要视图控制器时,我应该在哪里放置清理代码以释放资源? (它不能在 viewDidUnload 中,因为该方法已被弃用。它不能在 viewDidDisappear 中,因为其他东西可能持有对它的引用并最终会再次显示它。)


注意:如果您尝试在 Swift 中定义 dealloc 方法,则会收到以下错误:

Objective-C 选择器“dealloc”的方法“dealloc()”与具有相同 Objective-C 选择器的反初始化器冲突。

如果你的 Swift 视图控制器继承自一个 Objective-C 控制器,并且你在 Objective-C 的 dealloc 方法中放置了一个断点,你将得到与上面定义的相同的错误行为:deinit 将不会被调用,但dealloc 将被调用。

如果您尝试使用 Allocations 查看内存中类的实例数,两个版本显示相同的内容:# Persistent 始终为 1,并且每次显示第二个视图控制器时,# Transient 都会增加.

鉴于上述设置,不应有strong reference cycle 持有视图控制器。

【问题讨论】:

【参考方案1】:

TLDR:

只有在前面有可执行代码行的情况下,断点才会在deinit 中起作用。

如果您在可执行代码行上放置断点,那么它将起作用。 可执行代码行必须属于deinit方法。

感谢Adam for pointing me in the right direction。我没有进行广泛的测试,但看起来断点在deinit 中的行为与代码中其他任何地方的行为不同。

我将向您展示我在 每个 行号上添加断点的几个示例。那些将起作用的(例如暂停执行或执行其操作,例如记录消息)将通过 ➤ 符号指示。

通常断点会被大量命中,即使方法什么都不做:

➤ 1
➤ 2  func doNothing() 
➤ 3 
➤ 4  
  5

但是,在空白的deinit 方法中,NO 断点将永远被命中:

  1
  2  deinit 
  3 
  4  
  5

通过添加更多的代码行,我们可以看到它取决于断点后是否有可执行的代码行:

➤ 1 
➤ 2  deinit 
➤ 3      //
➤ 4      doNothing()
➤ 5      //
➤ 6      foo = "abc"
  7      //
  8  
  9

尤其要密切注意第 7 行和第 8 行,因为这与 doNothing() 的行为方式截然不同!

如果你习惯了doNothing() 中第 4 行的断点如何工作的这种行为,你可能会错误地推断如果你在这个例子中只有第 5 行(甚至第 4 行)的断点,你的代码没有执行:

➤ 1  
➤ 2  deinit 
➤ 3      number++
  4  //    incrementNumber()
  5  
  6

注意:对于在同一行暂停执行的断点,它们按照它们被创建的顺序被命中。为了测试它们的顺序,我将断点设置为 Log Message在评估操作后自动继续

注意:在我的测试中,还有另一个潜在的陷阱可能会让你陷入困境:如果你使用print("test"),它会弹出调试区域向你显示消息(消息以粗体显示)。但是,如果您添加断点并告诉它记录消息,它将以常规文本记录它并且弹出打开调试区域。您必须手动打开调试区域才能看到输出。

注意:这一切都在 Xcode 7.1.1 中测试过

【讨论】:

事实并非如此。不知道是什么场景。我添加了可执行行,但断点都不起作用,也没有打印日志。 (在 xcode 7.3 中测试) @DivineDesert:你确定你的视图控制器正在被释放吗? 谢谢。这搞砸了我半天的时间。【参考方案2】:

我还没有尝试过,但我确实为你找到了this:

似乎函数不会被调用,除非将一些代码放入 deinit(奇怪)必须是 swift 优化阶段的一部分。

尝试按照建议在 deinit 中添加打印语句并报告您的发现

【讨论】:

这是一个部分正确的解决方案。我将在我的发现中添加一个新的答案。不过,感谢您引导我朝着正确的方向前进! 请参阅***.com/a/33788952/35690以获得更详尽的解释!

以上是关于没有在 UIViewController 上调用 Deinit,但 Dealloc 是的主要内容,如果未能解决你的问题,请参考以下文章

drawRect 在 UIView 子类上被调用,但不在 UIViewController 上

UIViewController & UIview dealloc 没有被调用

什么时候在 UIView 或 UIViewController 上调用“required init?(coder aDecoder: NSCoder)”?

SwiftUI 在 UIViewController 中调用委托

UIViewController 未调用 viewDidLoad 且未创建 UIView 导致崩溃

没有 UIViewController 的 UIView