我正在尝试编写测试以检查保留周期但遇到了这种奇怪的行为。将视图控制器设置为UIViewController
时,不会取消分配nil
的属性。以此模拟对象为例:
class BasicViewController: UIViewController {
var someObject = NSObject()
.....
}
它拥有的只是一个变量。你会认为在调用basicViewController = nil
时会导致someObject
为零,但不是。
it("releases someObject") {
var controller: MockController? = MockController()
weak var something = controller?.something
expect(controller).toNot(beNil())
controller = nil
expect(controller).to(beNil())
expect(something).to(beNil())
}
it("doesn't release someObject") {
var controller: MockController? = MockController()
weak var something = controller?.something
expect(controller).toNot(beNil())
_ = controller?.view
controller = nil
expect(controller).to(beNil())
expect(something).toNot(beNil())
}
调用vc.view
时,会调用loadView
以及UIViewController
的生命周期函数 - viewDidLoad
,viewDidAppear
和viewWillAppear
。我的问题是为什么?为什么当我引用UIViewController
的{{1}}属性时,即使将view
设置为UIViewController
,UIViewController
拥有的所有对象仍然存在。
FWIW,我正在使用nil
和Quick
进行测试,以及Nimble
答案 0 :(得分:1)
简短回答:
添加一个autoreleasepool
,它将精确地指示何时从池中排出对象,并且它可以按照您的预期运行。
答案很长:
我遇到了你描述的相同行为。但问题不在于视图控制器的属性。它是视图控制器本身。
在您的示例中,您将controller
设置为nil
并使用现在为nil
的事实来推断控制器是否已被解除分配。但这仅测试对视图控制器的特定引用是否为nil
,但视图控制器本身可能尚未被释放。但您可以将weak var
测试与视图控制器本身一起使用。考虑一下这个视图控制器:
class BasicViewController: UIViewController {
// this is intentionally blank
}
我可以编写测试,其中视图控制器显示您描述的行为,加载视图后XCTAssertNil
测试失败的地方:
class MyApp2Tests: XCTestCase {
func testWithoutView() {
var controller: BasicViewController? = BasicViewController()
weak var weakController = controller
XCTAssertNotNil(weakController)
controller = nil
XCTAssertNil(weakController) // this succeeds
}
func testWithView() {
var controller: BasicViewController? = BasicViewController()
weak var weakController = controller
XCTAssertNotNil(weakController)
controller?.loadViewIfNeeded()
controller = nil
XCTAssertNil(weakController) // this fails
}
}
但是当我添加autoreleasepool
来明确控制池何时耗尽时,它按预期工作:
func testWithViewAndAutoreleasePool() {
weak var weakController: BasicViewController?
autoreleasepool {
var controller: BasicViewController? = BasicViewController()
weakController = controller
XCTAssertNotNil(weakController)
controller?.loadViewIfNeeded()
controller = nil
}
XCTAssertNil(weakController) // this succeeds
}
BTW,如果您正在寻找关于视图控制器释放时间的其他确认,请在print
中添加deinit
语句(以及您设置{{1}的位置并且你会看到controller = nil
的时间在做任何加载视图的事情时发生变化。
我无法解释这种行为。为什么要对deinit
做一些事情会影响视图控制器的生命周期?顺便说一下,我也用视觉控制器的属性执行了上述测试,就像在你的问题中一样,我看到完全相同的行为(但恕我直言,这并不奇怪,因为它只是因为视图控制器本身尚未被释放)。
至少我们可以使用view
显式控制自动释放池的生命周期时间。