ios - 关闭视图控制器时在应用程序中调用 deinit 但在单元测试中未调用
问题描述
我正在尝试对视图控制器的内存进行单元测试,以查看它们在被解雇时是否正确取消初始化。
class SettingsViewControllerTests: XCTestCase {
var controller: SettingsViewController!
override func setUp() {
super.setUp()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
controller = storyboard.instantiateViewController(withIdentifier: "SettingsViewController")
as? SettingsViewController
//load view hierarchy
_ = controller.view
}
func testLogout() {
let sideMenu = MockSideMenuViewController()
var navController: UINavigationController? = UINavigationController(rootViewController: sideMenu)
sideMenu.show(navController!, sender: nil)
navController?.pushViewController(controller, animated: true)
expect(navController).toNot(beNil())
controller.dismiss(animated: false, completion: nil)
expect(navController).toEventually(beNil(), timeout: 3) // fails
expect(self.controller).toEventually(beNil(), timeout: 3) // fails
}
在应用程序中,我使用 segue 来展示我的导航控制器 + SettingsViewController。它有一个调用dismiss
自身的方法,但是当我检查我的控制器实例时,它们仍然存在。在我的视图控制器中,我设置了一个打印语句来检查是否正在调用 deinit,并且当我通过应用程序上的步骤时,它确实被调用(两个控制器最终都转到nil
)。然而,单元测试并没有做同样的事情。我错过了什么?
解决方案
controller
是一个强烈持有的财产,所以你的测试是保留它。现在它只会在后续调用setUp()
. 如果您想controller.deinit
专门测试,请controller = nil
在测试中进行。
你可能会做这样的事情:
var controller: SettingsViewController! // <--- this is a strong ref
func testLogout() {
let sideMenu = MockSideMenuViewController()
// navController is a strong ref, held until the end of the scope; don't expect it to be nil
var navController: UINavigationController? = UINavigationController(rootViewController: sideMenu)
sideMenu.show(navController!, sender: nil)
navController?.pushViewController(controller, animated: true)
// hold a weak ref to your controller and then nil out its reference
weak var weakController = controller
// remove the strong reference
controller = nil
// popping will release the last reference
navController?.popViewController(animated: false)
expect(weakController).to(beNil(),) // succeeds
}
一些注意事项:
navController
是范围级别的变量。在函数结束之前它不会是 nil,所以没有理由测试或期望它。在你创建它之后它也肯定不会是 nil。UIViewController.dismiss(...)用于解除模式。您的控制器是导航堆栈的一部分。解雇不会做你所期望的。
感觉就像您期望var navController: UINavigationController?
自己变弱一样,但事实并非如此。 weak
vars 和 properties 应该是Optional
,但作为 Optional 并不意味着weak
。请参阅弱引用。
推荐阅读
- c# - 我将如何编写一个定时器,它可以作为协程正常工作,作为无效?
- r - 在 R 中的分组数据框中为每组分配增加的索引
- r - 如何加快“shuf -n dt.csv”并使用 data.table 设置列名?
- android - Android/Kotlin 教程的 gRPC 快速入门 - 服务器响应中不可用
- linux - Linux平台上的Focas fwlib32 CNC库
- spring-boot - 酒店搜索系统
- python - 从字典中删除重复项
- python - 我有很多 netcdf 文件,如何使用 xarray 将所有文件上传到一个 python 笔记本中?
- clojure - 根据这些值的向量,按地图中的值对地图向量进行排序 - Clojure
- javascript - document.title 元素在刷新时消失