I can't figure out why a UIViewController instantiated with UIStoryboard.instantiateViewController does not deinit when initalized in a unit test

Here's the code that I expect to deinit but does not:

func testDoesNotDeinit() throws {
    var strongViewController: ViewController? =
        UIStoryboard(
            name: "Main",
            bundle: Bundle(for: ViewController.self)
        )
        .instantiateViewController(withIdentifier: "ViewController") as! ViewController

    print("set nil")
    strongViewController = nil
    print("post set nil")
}

The result is:

set nil
post set nil
*** deinit *** // From deinit {} function in UIViewController

When the UIViewController is initialized programmatically it will deinitialize properly

func testDoesDeinit() throws {
    var strongViewController: ViewController? = ViewController()

    XCTAssertNotNil(strongViewController)
    print("set nil")
    strongViewController = nil
    print("post set nil")
    XCTAssertNil(strongViewController)
}

with result:

set nil
*** deinit *** // From deinit {} function in UIViewController
post set nil

Any ideas?

1

There are 1 best solutions below

0
On

I set a breakpoint at print("post set nil") and inspected the memory graph at that point in time. I saw that the VC is being retained by an instance of UIStoryboardScene:

enter image description here

UIStoryboardScene seems to be a private API. The only thing I can find about it is this header. This is probably an implementation detail of instantiateViewController.