ios - Deinit 未在 UIViewController 上调用,但 Dealloc 是

标签 ios objective-c swift memory-management uiviewcontroller

seems like the Swift equivalent of dealloc is deinit .但是,当您尝试在 UIViewController 上定义该方法时,它的行为并不像您期望的那样...

设置

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

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

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

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

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

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


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

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

如果您让 Swift View Controller 继承自 Objective-C Controller ,并在 Objective-C 的 dealloc 方法中放置一个断点,您将得到与上面定义的相同的错误行为:deinit不会被调用,但是 dealloc 会被调用。

如果您尝试使用 Allocations 查看内存中类的实例数,两个版本显示相同的内容:#Persistent 始终为 1,而 #Transient 每次显示第二个 View Controller 时都会增加。

鉴于上述设置,应该没有 strong reference cycle坚持 View Controller 。

最佳答案

简单描述:

只有在断点前面有可执行代码行时,断点才会在 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() 的行为有很大不同!

如果您习惯了第 4 行的断点在 doNothing() 中的工作方式,您可能会错误地推断如果您只有第 5 行的断点(甚至 4) 在这个例子中:

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

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

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

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

关于ios - Deinit 未在 UIViewController 上调用,但 Dealloc 是,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33112147/

相关文章:

ios - Swift 中的自定义 UITableViewCell 委托(delegate)模式

iphone - 如何添加到 NSDictionary

ios - "Supported Interface Orientations"有先例吗?

iOS:在来自不同 View 层次结构的 View 之上应用颜色效果

iphone - NSURLConnection 委托(delegate)方法没有被调用

iphone - 在 Interface Builder 中连接 iOS socket

ios - 使用新的苹果开发面板生成推送通知 ssl 证书和配置?

php - .htaccess 拒绝除 POST 之外的所有内容不适用于 Objective-C

swift - 如何从子类调用 MKTileOverlay "url"函数?获取 EXC_BAD_INSTRUCTION

swift - 使用 Swift 3.0 将 CoreData 保存到 Web 服务器