2016-12-23 3 views
1

Récemment, j'ai essayé de vérifier qu'un objet que j'ai écrit correctement se désalloue en utilisant un test unitaire. J'ai cependant trouvé que peu importe ce que j'essayais, l'objet ne se libère pas avant la fin du test. J'ai donc réduit le test à un exemple trivial (voir ci-dessous) qui tente de prouver les bases de la désallocation d'objets en utilisant des variables faibles. Dans mon esprit, la référence forte devrait arrêter de retenir l'objet après la sortie de la méthode de test, et la référence faible devrait être nulle lorsqu'elle est référencée sur la boucle d'exécution suivante. Cependant, la référence faible n'est jamais nulle et les deux tests échouent. Suis-je mal compris quelque chose ici? Voici les tests unitaires complets.Swift XCTest: vérification de la désallocation correcte des variables faibles

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) 
    } 
} 

Je pensais que peut-être désaffectation a été reporté en quelque sorte par XCTest, mais enveloppant même les corps des méthodes de test dans un autoreleasepool ne provoque pas l'objet à liberer.

+0

'weakMock' doit être en option, par exemple 'weak var weakMock: Mock? = strongMock'. Si elle ne peut pas être définie sur "nil", elle ne peut pas être libérée. – par

+0

@par 'weakMock' est automatiquement optionnel, ce qui est imposé par le compilateur. Si vous inspectez le type de 'weakMock', c'est déjà' Mock? 'Sans l'annotation de type explicite. –

+0

Et si vous le rendez explicitement optionnel devient-il nul? – par

Répondre

3

Le problème est que votre fonction testWeakVarDeallocation() n'a pas quitté lorsque le bloc dispatchAsync est appelé, donc une référence forte à strongMock est toujours conservée.

Essayez comme ça (ce qui permet de sortir testWeakVarDeallocation()) et vous verrez weakMock devient nil comme prévu:

class weakTestTests: XCTestCase { 
    var strongMock: Mock? = Mock() 

    func testWeakVarDeallocation() { 
     weak var weakMock = strongMock 

     print("weakMock is \(weakMock)") 

     let expt = self.expectation(description: "deallocated") 

     strongMock = nil 

     print("weakMock is now \(weakMock)") 

     DispatchQueue.main.async { 
      XCTAssertNil(weakMock)  // This assertion fails 

      print("fulfilling expectation") 
      expt.fulfill() 
     } 

     print("waiting for expectation") 
     self.waitForExpectations(timeout: 1.0, handler: nil) 
     print("expectation fulfilled") 
    } 
} 
+0

Vous avez raison de savoir pourquoi l'objet ne se libère pas, mais votre solution ne résout pas le problème. Le test se termine et passe avant que le premier bloc asynchrone ne soit exécuté, car aucune attente n'est attendue. Tout à fait le catch-22. –

+0

Vous avez raison, la fonction XCTest n'attend pas. J'ai mis à jour la réponse et il fait la bonne chose maintenant. – par

+0

Ouais cela fonctionne, j'ai compris le problème ci-dessous en utilisant autoreleasepool, mais votre solution est essentiellement la même. Merci pour l'aide! –