I have the following unit test:
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
}
The last two assertions are failing. Any way to reliably test for deallocation of UIViewController and UINavigationController?
As I recall, the docs say that objects held weakly "may be released at any time." The operative part is "may be".
I'm guessing that your view controller and navigation controller are auto-released. This is a term that harks back to the days of manual reference counting, but is still relevant under the covers. The object is created with a retain count of 1, and then added to an "auto-release pool". Then, next time your app's current function returns and it visits the event loop, the "auto-release pool is drained", which means that every entry in the auto-release pool gets a release message, dropping it's retain count by 1. When the retain count drops to zero, the object is deallocated.
(ARC actually uses reference counting under the covers, and so retain counts and auto-release pools are still relevant. It's just that with ARC the compiler takes care of maintaining them for you. Your strong and weak references get turned into to calls to the low level system retain, release, and auto-release functions.)
I'm not sure if it would work in the context of a test, but you might be able to use code like this:
That code would cause the calls to
XCTAssertNil()
to be deferred until the next pass through the event loop.The problem with that code is that by the time the call to
DispatchQueue.main.async()
is executed, the test may be over.Edit:
As pointed out by Cristik in their comment, the better way would be to use an autoreleasepool command: