UINavigationController 释放测试失败?
Testing for UINavigationController deallocation is failing?
我有以下单元测试:
func testReferences() throws {
var strongVC: UIViewController? = UIViewController()
var strongNC: UINavigationController? = UINavigationController(rootViewController: strongVC!)
weak var weakVC = strongVC
weak var weakNC = strongNC
strongVC = nil
XCTAssertNotNil(weakVC)
XCTAssertNotNil(weakNC)
strongNC = nil
XCTAssertNil(weakVC) // fails
XCTAssertNil(weakNC) // fails
}
最后两个断言失败了。有什么方法可以可靠地测试 UIViewController 和 UINavigationController 的释放?
我记得,文档说弱持有的对象“可能 随时被释放。”执行部分是“可能是”。
我猜您的视图控制器和导航控制器是 auto-released。这个术语可以追溯到手动引用计数的时代,但在幕后仍然相关。该对象创建时保留计数为 1,然后添加到“auto-release 池”。然后,下次你的应用程序的当前函数 returns 访问事件循环时,“auto-release 池被耗尽”,这意味着 auto-release 池中的每个条目都会收到一条释放消息,将其保留计数减 1。当保留计数降至零时,将释放该对象。
(ARC 实际上在幕后使用引用计数,因此保留计数和 auto-release 池仍然相关。只是对于 ARC,编译器会为您维护它们。您的强引用和弱引用变成对低级系统保留、释放和 auto-release 函数的调用。)
我不确定它是否适用于测试环境,但您可以使用如下代码:
func testReferences() throws {
var strongVC: UIViewController? = UIViewController()
var strongNC: UINavigationController? = UINavigationController(rootViewController: strongVC!)
weak var weakVC = strongVC
weak var weakNC = strongNC
strongVC = nil
XCTAssertNotNil(weakVC)
XCTAssertNotNil(weakNC)
strongNC = nil
DispatchQueue.main.async {
XCTAssertNil(weakVC) // fails
XCTAssertNil(weakNC) // fails
}
}
}
该代码会导致对 XCTAssertNil()
的调用被推迟到下一次通过事件循环。
该代码的问题在于,在执行对 DispatchQueue.main.async()
的调用时,测试可能已经结束。
编辑:
正如 Cristik 在他们的评论中指出的那样,更好的方法是使用 autoreleasepool 命令:
func testReferences() throws {
//Define the vars we want to test outside of the auto-release pool statement.
weak var weakVC: UIViewController
weak var weakNC: UINavigationController
autoreleasepool {
var strongVC: UIViewController? = UIViewController()
var strongNC: UINavigationController? = UINavigationController(rootViewController: strongVC!)
weakVC = strongVC
weakNC = strongNC
strongVC = nil
XCTAssertNotNil(weakVC)
XCTAssertNotNil(weakNC)
strongNC = nil
}
//Test for nil outside of the autorelasepool statement,
//after the auto-release pool is drained.
XCTAssertNil(weakVC) // fails
XCTAssertNil(weakNC) // fails
}
我有以下单元测试:
func testReferences() throws {
var strongVC: UIViewController? = UIViewController()
var strongNC: UINavigationController? = UINavigationController(rootViewController: strongVC!)
weak var weakVC = strongVC
weak var weakNC = strongNC
strongVC = nil
XCTAssertNotNil(weakVC)
XCTAssertNotNil(weakNC)
strongNC = nil
XCTAssertNil(weakVC) // fails
XCTAssertNil(weakNC) // fails
}
最后两个断言失败了。有什么方法可以可靠地测试 UIViewController 和 UINavigationController 的释放?
我记得,文档说弱持有的对象“可能 随时被释放。”执行部分是“可能是”。
我猜您的视图控制器和导航控制器是 auto-released。这个术语可以追溯到手动引用计数的时代,但在幕后仍然相关。该对象创建时保留计数为 1,然后添加到“auto-release 池”。然后,下次你的应用程序的当前函数 returns 访问事件循环时,“auto-release 池被耗尽”,这意味着 auto-release 池中的每个条目都会收到一条释放消息,将其保留计数减 1。当保留计数降至零时,将释放该对象。
(ARC 实际上在幕后使用引用计数,因此保留计数和 auto-release 池仍然相关。只是对于 ARC,编译器会为您维护它们。您的强引用和弱引用变成对低级系统保留、释放和 auto-release 函数的调用。)
我不确定它是否适用于测试环境,但您可以使用如下代码:
func testReferences() throws {
var strongVC: UIViewController? = UIViewController()
var strongNC: UINavigationController? = UINavigationController(rootViewController: strongVC!)
weak var weakVC = strongVC
weak var weakNC = strongNC
strongVC = nil
XCTAssertNotNil(weakVC)
XCTAssertNotNil(weakNC)
strongNC = nil
DispatchQueue.main.async {
XCTAssertNil(weakVC) // fails
XCTAssertNil(weakNC) // fails
}
}
}
该代码会导致对 XCTAssertNil()
的调用被推迟到下一次通过事件循环。
该代码的问题在于,在执行对 DispatchQueue.main.async()
的调用时,测试可能已经结束。
编辑:
正如 Cristik 在他们的评论中指出的那样,更好的方法是使用 autoreleasepool 命令:
func testReferences() throws {
//Define the vars we want to test outside of the auto-release pool statement.
weak var weakVC: UIViewController
weak var weakNC: UINavigationController
autoreleasepool {
var strongVC: UIViewController? = UIViewController()
var strongNC: UINavigationController? = UINavigationController(rootViewController: strongVC!)
weakVC = strongVC
weakNC = strongNC
strongVC = nil
XCTAssertNotNil(weakVC)
XCTAssertNotNil(weakNC)
strongNC = nil
}
//Test for nil outside of the autorelasepool statement,
//after the auto-release pool is drained.
XCTAssertNil(weakVC) // fails
XCTAssertNil(weakNC) // fails
}