问题描述
似乎 dealloc
的 Swift 等价物是 deinit
.但是,当您尝试在 UIViewController 上定义该方法时,它的行为与您预期的不同...
It seems like the Swift equivalent of dealloc
is deinit
. However, when you attempt to define the method on a UIViewController, it doesn't behave as you would expect...
设置
- 使用 Xcode 7.0 在 Swift 或 Objective-C 中创建一个新的单一视图项目.
- 在使用故事板创建的视图控制器上添加一个关闭"按钮(我将此视图控制器称为 VC2;它的类是 ViewController).
- 添加一个新的视图控制器并将其设置为初始视图控制器(VC1,类为零).
- 向 VC1 添加一个present"按钮,并为 VC2 添加一个Present Modally"segue.
- 在 VC2 的代码中,在
deinit
(Swift) 或dealloc
(Objective-C) 中放置一个断点. 在 VC2 中,使关闭"按钮的动作执行以下操作:
- Create a new Single View project using Xcode 7.0, in either Swift or Objective-C.
- Add a "dismiss" button on the view controller that was created with the storyboard (I'll refer to this view controller as VC2; its class is ViewController).
- Add a new view controller and set it to be the initial view controller (VC1, class is nil).
- Add a "present" button to VC1 with a "Present Modally" segue to VC2.
- In VC2's code, put a breakpoint in
deinit
(Swift) ordealloc
(Objective-C). In VC2, make the "dismiss" button's action do the following:
// Swift:
presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
// Objective-C:
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
注意如何在 Objective-C 中dealloc
断点被命中.
Notice how in Objective-C, the dealloc
breakpoint is hit.
另一方面,在 Swift 中,deinit
断点永远不会被命中.
In Swift, on the other hand, the deinit
breakpoint is never hit.
为什么从不调用 deinit
? 这是错误还是设计使然?
Why is deinit
never called? Is this a bug or by design?
如果这是设计使然,当不再需要视图控制器时,我应该将清理代码放在哪里以释放资源?(它不能在 viewDidUnload
中,因为该方法已被弃用.它不能在 viewDidDisappear
中,因为其他东西可能持有对它的引用并最终会显示它再次.)
If this is by design, where should I put clean up code to free up resources when the view controller will no longer be needed? (It can't be in viewDidUnload
since that method is deprecated. It can't be in viewDidDisappear
because something else might be holding a reference to it and will eventually show it again.)
注意:如果您尝试在 Swift 中定义 dealloc
方法,则会出现以下错误:
Note: If you attempt to define a dealloc
method in Swift, you get the following error:
带有 Objective-C 选择器dealloc"的方法dealloc()"与带有相同 Objective-C 选择器的析构器冲突.
如果你的 Swift 视图控制器继承自一个 Objective-C 控制器,并且你在 Objective-C 的 dealloc 方法中放置了一个断点,你将得到与上面定义的相同的错误行为:deinit
不会被调用,但是 dealloc
会被调用.
If you have the Swift view controller inherit from an Objective-C controller, and you put a breakpoint in the Objective-C dealloc method, you will get the same buggy behavior defined above: the deinit
will not be called, but the dealloc
will be called.
如果您尝试使用 Allocations 查看内存中类的实例数,则两个版本都显示相同的内容:# Persistent
始终为 1,# Transient
每次显示第二个视图控制器时都会增加.
If you attempt to use Allocations to view the number of instances of the class in memory, both versions show the same thing: The # Persistent
is always 1, and the # Transient
increases each time you show the second view controller.
鉴于上述设置,应该没有强引用循环抓住视图控制器.
Given the above setup, there should be no strong reference cycle holding on to the view controller.
推荐答案
TLDR:
- 如果在一行可执行代码上放置一个断点,它就会起作用.
- 可执行代码行必须属于
deinit
方法.
感谢亚当为我指明了正确的方向.我没有进行大量测试,但看起来断点在 deinit
中的行为与代码中的其他任何地方都不同.
Thanks to Adam for pointing me in the right direction. I didn't do extensive tests, but it looks like breakpoints behave differently in deinit
than everywhere else in your code.
我将向您展示几个示例,其中我在每个行号上添加了一个断点.那些将起作用的(例如暂停执行或执行其操作,例如记录消息)将通过 ➤ 符号指示.
I will show you several examples where I added a breakpoint on each line number. Those that will work (e.g. pause execution or perform their action such as logging a message) will be indicated via the ➤ symbol.
通常断点会被大量命中,即使一个方法什么都不做:
Normally breakpoints are hit liberally, even if a method does nothing:
➤ 1
➤ 2 func doNothing() {
➤ 3
➤ 4 }
5
然而,在一个空白的 deinit
方法中,NO 断点永远不会被命中:
However, in a blank deinit
method, NO breakpoints will ever get hit:
1
2 deinit {
3
4 }
5
通过添加更多的代码行,我们可以看到它取决于断点后是否有可执行的代码行:
By adding more lines of code, we can see that it depends on if there is an executable line of code following the breakpoint:
➤ 1
➤ 2 deinit {
➤ 3 //
➤ 4 doNothing()
➤ 5 //
➤ 6 foo = "abc"
7 //
8 }
9
尤其要密切注意第 7 行和第 8 行,因为这与 doNothing()
的行为方式大不相同!
In particular, play close attention to lines 7 and 8, since this differs significantly from how doNothing()
behaved!
如果您已经习惯了 doNothing()
中第 4 行的断点如何工作的这种行为,那么您可能会错误地推断如果您只有第 5 行的断点(甚至 4) 在这个例子中:
If you got used to this behavior of how the breakpoint on line 4 worked in doNothing()
, you may incorrectly deduce that your code is not executing if you only had a breakpoint on line 5 (or even 4) in this example:
➤ 1
➤ 2 deinit {
➤ 3 number++
4 // incrementNumber()
5 }
6
这篇关于未在 UIViewController 上调用 Deinit,但 Dealloc 是的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!