Recently I was attempting to verify that an object I wrote properly deallocates using a unit test. I found however that no matter what I tried the object would not deallocate before the test completed. So I reduced the test to a trivial example (seen below) that attempts to prove the basics of object deallocation using weak variables.
In my mind, the strong reference should stop retaining the object after the test method exits, and the weak reference should be nil when referenced on the next run loop. However, the weak reference is never nil and both tests fail. Am I misunderstanding something here? Below are the unit tests in full.
class Mock { //class type, should behave with reference semantics
init() { }
}
class DeallocationTests: XCTestCase {
func testWeakVarDeallocation() {
let strongMock = Mock()
weak var weakMock: Mock? = strongMock
let expt = expectation(description: "deallocated")
DispatchQueue.main.async {
XCTAssertNil(weakMock) //This assertion fails
expt.fulfill()
}
waitForExpectations(timeout: 1.0, handler: nil)
}
func testCaptureListDeallocation() {
let strongMock = Mock()
let expt = expectation(description: "deallocated")
DispatchQueue.main.async { [weak weakMock = strongMock] in
XCTAssertNil(weakMock) //This assertion also fails
expt.fulfill()
}
waitForExpectations(timeout: 1.0, handler: nil)
}
}
I thought that maybe deallocation was being deferred somehow by XCTest, but even wrapping the test method bodies in an autoreleasepool
did not cause the object to deallocate.
The problem is that your
testWeakVarDeallocation()
function hasn't exited when thedispatchAsync
block is called so a strong reference tostrongMock
is still held.Try it like this (allowing
testWeakVarDeallocation()
to exit) and you'll seeweakMock
becomesnil
as expected: