ios Deinit 未在 UIViewController 上调用,但 Dealloc 是
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33112147/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Deinit not called on a UIViewController, but Dealloc is
提问by Senseful
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...
它看起来像雨燕相当于dealloc
ISdeinit
。但是,当您尝试在 UIViewController 上定义该方法时,它的行为并不像您预期的那样......
Setup
设置
- 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];
- Run the app and tap both buttons to first present VC2 and then dismiss it.
- 使用 Xcode 7.0 在 Swift 或 Objective-C 中创建一个新的 Single View 项目。
- 在使用故事板创建的视图控制器上添加一个“关闭”按钮(我将此视图控制器称为 VC2;它的类是 ViewController)。
- 添加一个新的视图控制器并将其设置为初始视图控制器(VC1,类为零)。
- 将“present”按钮添加到 VC1,并为 VC2 设置“Present Modally”。
- 在 VC2 的代码中,在
deinit
(Swift) 或dealloc
(Objective-C) 中放置一个断点。 在 VC2 中,使“关闭”按钮的动作执行以下操作:
// Swift: presentingViewController?.dismissViewControllerAnimated(true, completion: nil) // Objective-C: [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
- 运行应用程序并点击两个按钮首先显示 VC2,然后关闭它。
Notice how in Objective-C, the dealloc
breakpoint is hit.
请注意,在Objective-C中,该dealloc
断点被击中。
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?
为什么deinit
从不调用?这是错误还是设计使然?
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.)
如果这是设计使然,当不再需要视图控制器时,我应该将清理代码放在哪里以释放资源?(它不能存在,viewDidUnload
因为该方法已被弃用。它不能存在,viewDidDisappear
因为其他东西可能持有对它的引用并最终会再次显示它。)
Note: If you attempt to define a dealloc
method in Swift, you get the following error:
注意:如果您尝试dealloc
在 Swift 中定义一个方法,您会收到以下错误:
Method 'dealloc()' with Objective-C selector 'dealloc' conflicts with deinitializer with the same Objective-C selector.
方法 'dealloc()' 与 Objective-C 选择器 'dealloc' 与具有相同 Objective-C 选择器的析构器冲突。
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.
如果你的 Swift 视图控制器继承自一个 Objective-C 控制器,并且你在 Objective-C 的 dealloc 方法中放置了一个断点,你将得到与上面定义的相同的错误行为:deinit
不会被调用,但dealloc
会被调用。
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.
如果您尝试使用 Allocations 查看内存中类的实例数,则两个版本都显示相同的内容:# Persistent
始终为 1,并且# Transient
每次显示第二个视图控制器时都会增加。
Given the above setup, there should be no strong reference cycleholding on to the view controller.
鉴于上述设置,视图控制器不应该有强引用循环。
回答by Senseful
TLDR:
域名注册地址:
Breakpoints will only work in deinit
if there is an executable line of code ahead of them.
只有在断点deinit
前面有一行可执行代码时,断点才会起作用。
- If you place a breakpoint on a line of executable code, then it will work.
- The executable line of code must belong to the
deinit
method.
- 如果你在一行可执行代码上放置一个断点,那么它就会起作用。
- 可执行代码行必须属于该
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.
感谢Adam 为我指明了正确的方向。我没有进行大量测试,但看起来断点的行为与deinit
代码中的其他任何地方都不同。
I will show you several examples where I added a breakpoint on eachline 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
However, in a blank deinit
method, NObreakpoints will ever get hit:
然而,在一个空白的deinit
方法,无断点永远不会被打到:
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
In particular, play close attention to lines 7 and 8, since this differs significantly from how doNothing()
behaved!
尤其要密切注意第 7 行和第 8 行,因为这与doNothing()
行为方式有很大不同!
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:
如果您习惯了第 4 行上的断点如何在 中工作的这种行为,那么doNothing()
在此示例中,如果您仅在第 5 行(甚至第 4 行)上有一个断点,您可能会错误地推断出您的代码没有执行:
? 1
? 2 deinit {
? 3 number++
4 // incrementNumber()
5 }
6
Note: for breakpoints that pause execution on the same line, they are hit in the order that they were created. To test their order, I set a breakpoint to Log Messageand Automatically continue after evaluating actions.
注意:对于在同一行上暂停执行的断点,它们按照创建的顺序被命中。为了测试它们的顺序,我将断点设置为Log Message并在评估 actions 后自动继续。
Note: In my testing there was also another potential pitfall that might get you: If you use print("test")
, it will pop up the Debug Area to show you the message (the message appears in bold). However, if you add a breakpoint and tell it to Log Message, it will log it in regular text and notpop open the Debug Area. You have to manually open up the Debug Area to see the output.
注意:在我的测试中,还有另一个可能会让您陷入困境的潜在陷阱:如果您使用print("test")
,它会弹出调试区域以向您显示消息(该消息以粗体显示)。但是,如果您添加断点并将其告知Log Message,它将以常规文本形式记录它,并且不会弹出调试区域。您必须手动打开调试区域才能看到输出。
Note: This was all tested in Xcode 7.1.1
注意:这都是在 Xcode 7.1.1 中测试过的
回答by Adam Campbell
I haven't tried it yet but I did find thisfor you:
我还没有尝试过,但我确实为你找到了这个:
It seems the function won't be called unless some code is put inside the deinit (weird) must be part of swift's optimisation stage.
除非将某些代码放入 deinit(奇怪)必须是 swift 优化阶段的一部分,否则该函数似乎不会被调用。
Try putting a print statement inside your deinit as suggested and report your findings
尝试按照建议将打印语句放入 deinit 并报告您的发现