Me encontré con lo más raro, tal vez alguien tenga una explicación. Pasos:
Si omite los pasos 3 y 4, la referencia débil se vuelve nula como se esperaba.
deinit class TestView: UIView { deinit { print("deinit") } } class RetainTests: XCTestCase { func testRetainFails() { let holderView = UIView() var element: TestView? = TestView() holderView.addSubview(element!) element?.removeFromSuperview() weak var weakElement = element XCTAssertNotNil(weakElement) // after next line `weakElement` should be nil element = nil // console will print "deinit" XCTAssertNil(weakElement) // fails! } func testRetainPasses() { var element: TestView? = TestView() weak var weakElement = element XCTAssertNotNil(weakElement) // after next line `weakElement` should be nil element = nil // console will print "deinit" XCTAssertNil(weakElement) } } Ambas pruebas imprimirán deinit en la consola, pero en el caso de que falle la prueba, el weakElement aún conserva la referencia si el element estuvo en una jerarquía de vista en algún momento, aunque el element se desasignó con éxito. ¿Como puede ser?
(Editar: esto está dentro de una aplicación, NO un patio de recreo)
Hay un lanzamiento automático adjunto a TestView cuando lo agrega y lo elimina de la supervista. Si se asegura de drenar el grupo de liberación automática, esta prueba se comporta como espera:
autoreleasepool { holderView.addSubview(element!) element?.removeFromSuperview() } Dicho esto, no puedes confiar en este comportamiento. No hay ninguna promesa sobre cuándo se destruirá un objeto. Solo sabe que será después de que publique su última referencia fuerte a él. Puede haber retenciones o liberaciones automáticas adicionales en el objeto. Y no hay ninguna promesa de que se llamará a deinit , así que definitivamente asegúrese de no poner ninguna lógica obligatoria allí. Solo debe realizar la limpieza de la memoria. (Tanto Mac como iOS, por razones ligeramente diferentes, nunca llamen a deinit durante la finalización del programa).
En su caso de prueba fallido, deinit se imprime después de que falla la afirmación. Puede demostrar esto colocando puntos de interrupción en la aserción fallida y la print . Esto se debe a que el grupo de liberación automática se vacía al final del caso de prueba.
La lección subyacente es que es muy común que las llamadas de retención/liberación automática equilibradas se coloquen en un objeto que se pasa al código ObjC (y UIKit es en gran medida ObjC). Por lo general, no debe esperar que los objetos UIKit se destruyan antes del final del ciclo de eventos actual. No confíe en eso (no se promete), pero a menudo es cierto.