没有在 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 中调用委托