未在 UIViewController 上调用 Deinit,但 Dealloc 是 [英] Deinit not called on a UIViewController, but Dealloc is

查看:16
本文介绍了未在 UIViewController 上调用 Deinit,但 Dealloc 是的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

似乎 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...

设置

  1. 使用 Xcode 7.0 在 Swift 或 Objective-C 中创建一个新的单一视图项目.
  2. 在使用故事板创建的视图控制器上添加一个关闭"按钮(我将此视图控制器称为 VC2;它的类是 ViewController).
  3. 添加一个新的视图控制器并将其设置为初始视图控制器(VC1,类为零).
  4. 向 VC1 添加一个present"按钮,并为 VC2 添加一个Present Modally"segue.
  5. 在 VC2 的代码中,在 deinit (Swift) 或 dealloc (Objective-C) 中放置一个断点.
  6. 在 VC2 中,使关闭"按钮的动作执行以下操作:

  1. Create a new Single View project using Xcode 7.0, in either Swift or Objective-C.
  2. 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).
  3. Add a new view controller and set it to be the initial view controller (VC1, class is nil).
  4. Add a "present" button to VC1 with a "Present Modally" segue to VC2.
  5. In VC2's code, put a breakpoint in deinit (Swift) or dealloc (Objective-C).
  6. 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];

  • 运行应用并点按两个按钮,首先显示 VC2,然后关闭它.
  • 注意如何在 Objective-Cdealloc 断点被命中.

    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 选择器的析构器冲突.

    Method 'dealloc()' with Objective-C selector 'dealloc' conflicts with deinitializer with the same Objective-C selector.

    如果你的 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
    

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

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

    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 not pop open the Debug Area. You have to manually open up the Debug Area to see the output.

    注意:这都是在 Xcode 7.1.1 中测试的

    这篇关于未在 UIViewController 上调用 Deinit,但 Dealloc 是的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆