web-dev-qa-db-fra.com

Deinit n'a pas fait appel à un UIViewController, mais Dealloc est

Il ressemble à Swift équivalent de dealloc est deinit . Cependant, lorsque vous essayez de définir la méthode sur un UIViewController, il ne se comporte pas comme prévu ...

Configuration

  1. Créez un nouveau projet de vue unique à l'aide de Xcode 7.0, dans Swift ou Objective-C.
  2. Ajoutez un bouton "ignorer" sur le contrôleur de vue qui a été créé avec le storyboard (je vais appeler ce contrôleur de vue VC2; sa classe est ViewController).
  3. Ajoutez un nouveau contrôleur de vue et définissez-le comme contrôleur de vue initial (VC1, la classe est nulle).
  4. Ajoutez un bouton "présent" à VC1 avec une séquence "Présent modalement" à VC2.
  5. Dans le code de VC2, placez un point d'arrêt dans deinit (Swift) ou dealloc (Objective-C).
  6. Dans VC2, effectuez l'action du bouton "ignorer" comme suit:

    // Swift:
    presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
    
    // Objective-C:
    [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
    
  7. Exécutez l'application et appuyez sur les deux boutons pour présenter d'abord VC2, puis fermez-le.

Remarquez comment dans Objective-C , le point d'arrêt dealloc est atteint .

Dans Swift , d'autre part, le point d'arrêt deinit n'est jamais atteint .

Pourquoi deinit n'est-il jamais appelé? Est-ce un bug ou par conception?

Si c'est de par leur conception, où dois-je mettre du code de nettoyage pour libérer des ressources lorsque le contrôleur de vue ne sera plus nécessaire? (Elle ne peut pas être dans viewDidUnload car cette méthode est obsolète. Elle ne peut pas être dans viewDidDisappear parce que quelque chose d'autre pourrait contenir une référence et l'affichera éventuellement à nouveau.)


Remarque: Si vous essayez de définir une méthode dealloc dans Swift, vous obtenez l'erreur suivante:

La méthode 'dealloc ()' avec le sélecteur Objective-C 'dealloc' entre en conflit avec le déinitialiseur avec le même sélecteur Objective-C.

Si vous avez le contrôleur de vue Swift hérité d'un contrôleur Objective-C, et que vous placez un point d'arrêt dans la méthode dealloc Objective-C, vous obtiendrez le même comportement de bogue défini ci-dessus: le deinit ne sera pas appelé, mais le dealloc sera appelé.

Si vous essayez d'utiliser des allocations pour afficher le nombre d'instances de la classe en mémoire, les deux versions affichent la même chose: le # Persistent est toujours 1 et le # Transient augmente chaque fois que vous affichez le deuxième contrôleur de vue.

Compte tenu de la configuration ci-dessus, il ne devrait pas y avoir cycle de référence fort accroché au contrôleur de vue.

19
Senseful

TLDR:

Les points d'arrêt ne fonctionneront dans deinit que s'il y a une ligne de code exécutable devant eux.

  • Si vous placez un point d'arrêt sur une ligne de code exécutable, cela fonctionnera.
  • La ligne de code exécutable doit appartenir à la méthode deinit.

Merci à Adam de m'avoir pointé dans la bonne direction . Je n'ai pas fait de tests approfondis, mais il semble que les points d'arrêt se comportent différemment dans deinit que partout ailleurs dans votre code.

Je vais vous montrer plusieurs exemples où j'ai ajouté un point d'arrêt sur chaque numéro de ligne. Ceux qui fonctionneront (par exemple, interrompre l'exécution ou effectuer leur action, comme enregistrer un message) seront indiqués via le symbole ➤.

Normalement, les points d'arrêt sont généreusement atteints, même si une méthode ne fait rien:

➤ 1
➤ 2  func doNothing() {
➤ 3 
➤ 4  }
  5

Cependant, dans une méthode deinit vide, [~ # ~] aucun [~ # ~] point d'arrêt ne sera jamais atteint:

  1
  2  deinit {
  3 
  4  }
  5

En ajoutant plus de lignes de code, nous pouvons voir que cela dépend si une ligne de code exécutable suit le point d'arrêt:

➤ 1 
➤ 2  deinit {
➤ 3      //
➤ 4      doNothing()
➤ 5      //
➤ 6      foo = "abc"
  7      //
  8  }
  9

En particulier, portez une attention particulière aux lignes 7 et 8, car cela diffère considérablement de la façon dont doNothing() s'est comporté!

Si vous vous êtes habitué à ce comportement du fonctionnement du point d'arrêt sur la ligne 4 dans doNothing(), vous pouvez déduire à tort que votre code ne s'exécute pas si vous n'aviez qu'un point d'arrêt sur la ligne 5 (ou même 4) dans ce exemple:

➤ 1  
➤ 2  deinit {
➤ 3      number++
  4  //    incrementNumber()
  5  }
  6

Remarque: pour les points d'arrêt qui interrompent l'exécution sur la même ligne, ils sont affichés dans l'ordre de leur création. Pour tester leur commande, j'ai défini un point d'arrêt sur Message de journal et Continuer automatiquement après avoir évalué les actions.

Remarque: Lors de mes tests, il y avait également un autre piège potentiel qui pourrait vous amener: Si vous utilisez print("test"), il apparaîtra dans la zone de débogage pour vous montrer le message (le message apparaît en gras). Cependant, si vous ajoutez un point d'arrêt et que vous le dites à Consigner le message , il le consignera en texte normal et pas ouvrez la zone de débogage. Vous devez ouvrir manuellement la zone de débogage pour voir la sortie.

Remarque: Tout cela a été testé dans Xcode 7.1.1

30
Senseful

Je ne l'ai pas encore essayé mais j'ai trouvé this pour vous:

Il semble que la fonction ne sera pas appelée à moins qu'un code ne soit placé à l'intérieur du deinit (bizarre) doit faire partie de l'étape d'optimisation de Swift.

Essayez de mettre une déclaration imprimée à l'intérieur de votre deinit comme suggéré et signalez vos résultats

5
Adam Campbell