web-dev-qa-db-fra.com

Dealloc n'est pas appelé sur l'application ARC

J'ai un UIViewController qui est poussé sur un contrôleur de conteneur puis sauté, et en utilisant l'instrument d'allocation, je peux voir que le contrôleur de vue est détruit par la suite. Cependant, un point d'arrêt dans le dealloc du contrôleur n'est jamais atteint. Est-ce que quelqu'un sait pourquoi dealloc n'est pas appelé? Est-il possible pour ARC de détruire un objet sans appeler dealloc?

De plus, j'ai désactivé NSZombies (certains ont dit que cela pouvait empêcher le déclenchement du dealloc).

Modifier:

Dealloc ne fait pas grand-chose, imprime simplement sur la console, et il n'est jamais appelé:

- (void)dealloc { NSLog(@"Deallocating..."); }

Je ne peux pas poster le contrôleur de conteneur - il est propriétaire et trop compliqué. Dealloc est appelé de manière cohérente sur certains contrôleurs et pas sur d'autres. Si je peux trouver le temps, je vais essayer de publier une version simplifiée qui reproduit le problème.

Existe-t-il un moyen de vérifier que NSZombies est désactivé?

Edit2

Je poste une capture d'écran d'instruments; il me semble que la désallocation est correcte.

enter image description here

38
rob

Je viens de rencontrer un problème similaire. Avez-vous des blocs où vous faites référence à "soi"? J'avais quelques blocs pour l'observation des notifications dans mon init où je faisais référence à "soi". Sous ARC, l'auto est conservé en blocs. Mon dealloc n'a pas été appelé et c'est là que je supprimais l'observation.

L'astuce consiste à créer une référence __weak (iOS 5+) ou __unsafe_unretained (iOS 4.x) à votre "self" et à l'utiliser pour accéder à self ou à n'importe quel _iVars (cela entraînera également la conservation de "self") dans le bloquer. Voici un exemple.

__unsafe_unretained TableViewController *weakSelf = self;
[[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextObjectsDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
        if (weakSelf.tableView) {
            [weakSelf.tableView reloadData];
        }
    }];
92
mikeq

Dans mon cas, c'était NSTimer. Il conserve sa cible, vous devez donc invalider le minuteur lorsque vous avez terminé avec le contrôleur de vue.

23
Almas Sapargali

Si dealloc n'est pas appelé par le VC, alors je parierais qu'il y a une référence circulaire quelque part dans votre code, ce qui empêche ARC d'appeler dealloc.

Quelques points à vérifier:

  1. Avez-vous un objet instancié qui fait référence au VC?
  2. Si vous devez faire référence à VC assurez-vous que vous avez utilisé l'attribut '__unsafe_unretained' ou 'faible' (iOS5 +) afin que le cycle de rétention ne se produise pas.

J'ai été étouffé dans le cul quand mes déclarations de délégué n'ont pas utilisé __unsafe_unretained.

20
Dean Liu

Même avec ARC, vous pouvez inspecter le nombre de références manuellement:

CFIndex rc = CFGetRetainCount((__bridge CFTypeRef)myObj);

Vous pouvez savoir avec certitude si votre code est bloqué dans un cycle de mémoire.

16
jbbenni

Mon problème était les délégués.

Vérifiez vos délégués! La propriété déléguée doit avoir weak spécifié:

weak var delegate: SomeProtocol?

ou

@property (weak, nonatomic) id<SomeProtocol> delegate;

15
kgaidis

Voici un autre conseil (qui m'est arrivé):

tl; dr: Regardez également les variables d'instance de votre classe, pas seulement les propriétés de la classe. Allouez-vous une variable d'instance dans le code et ne la définissez-vous pas sur nil plus tard?

J'avais une classe avec un tas de propriétés (@property (nonatomic, strong) et @property (nonatomic, weak)) dans son fichier d'en-tête. J'ai commenté ces propriétés une par une pour voir si cela allait changer quoi que ce soit. Il n'a pas. La classe principale n'était toujours pas délocalisée. Le problème n'était donc pas dans les propriétés.

Ce que j'ai fait ensuite, j'ai regardé les variables d'instance. J'avais une variable d'instance (qui était un UIViewController), que j'ai créée sur viewDidLoad. Celui-ci n'a jamais été désalloué!

Quand j'ai supprimé cette variable, bien sûr, ma classe principale s'appelait dealloc.

Donc, parce que celui-ci n'était pas désalloué, ma classe principale n'était pas non plus désallouée. Le déplacement de cette variable d'instance en tant que propriété a résolu le problème pour moi.

Question: Je ne sais pas pourquoi cela se produit cependant. Le système d'exploitation contrôle-t-il mieux les propriétés d'une classe que les variables d'instance? Il semble un peu bizarre que la classe parente qui contient la variable d'instance ne soit pas libérée à cause de sa variable d'instance.

10
Andrei

Avez-vous NSZombieEnabled? Voir cette question:

Pourquoi l'objet n'est-il pas désalloué lors de l'utilisation de ARC + NSZombieEnabled

J'ai été perplexe pendant un moment moi-même ...

5
TomSwift