1

Voici un problème intéressant. J'ai un contrôleur de vue, appelons-le MyViewController. L'un de ses membres est une vue personnalisée, appelons cela MyCustomView. MyCustomView a également une référence à son parent, MyViewController, car MyViewController est utilisé en tant que délégué pour la vue personnalisée. Le problème se produit lorsque MyViewController est déchargé, par exemple en raison d'un avertissement de mémoire. Voici ce qui se passe:Comment libérer les contrôleurs de vue contenant une vue qui fait référence à son parent

Tout d'abord, viewDidUnload est appelé pour MyViewController. Il ressemble à ceci:

- (void)viewDidUnload { 
[super viewDidUnload]; 
    self.myCustomView = nil; 
    self.someData = nil; 
    ... 
} 

Lorsque self.myCustomView = nul est exécuté, il déclenche myCustomView être désallouée. La routine dealloc de MyCustomView ressemble à ceci:

- (void)dealloc { 
    ... 
    [delegate release]; 
    ... 
    [super dealloc]; 
} 

Rappel ci-dessus que le délégué est MyViewController. Si MyCustomView était publié en premier, cela ne poserait pas de problème, car le nombre de références pour MyViewController serait supérieur à 1, mais dans ce cas MyViewController ne contient aucune autre référence. Cela provoque MyViewController d'être libéré, à savoir c'est la routine dealloc est appelée, qui ressemble à:

- (void)dealloc { 
    [myCustomView release]; 
    [somedata release]; 
    ... 
    [super dealloc]; 
} 

Comme vous pouvez le voir, les membres de MyViewController, à savoir, « somedata » se libérer AVANT la routine viewDidUnload pour MyViewController, battant jamais . Comme les routines de dealloc pour MyViewController et MyCustomView complet et nous rentrerons à la finition de la routine viewDidUnload, nous arrivons à la ligne

self.somedata = nil; 

Maintenant, somedata n'est pas nulle, mais sa valeur a déjà été communiqué! Cela provoque une exception.

Cela semble être un défaut critique dans le comptage des références. Comment gérez-vous les références bi-directionnelles dans des objets comme celui-ci qui se libèrent mutuellement?

Une réponse est de toujours définir les membres comme étant nuls dans la routine dealloc. Je n'aime pas cette réponse. C'est beaucoup de travail supplémentaire et ne devrait pas être nécessaire la plupart du temps. L'autre réponse consiste à réorganiser l'ordre de viewDidUnload de sorte que la désallocation des objets enfants qui peuvent avoir des pointeurs vers l'arrière arrive toujours à la fin. Je n'aime pas non plus cette solution et elle pourrait même ne pas fonctionner de temps en temps.

Comment contournez-vous ce problème?

Répondre

5

Les membres délégués ne sont généralement pas comptés par référence. Généralement la déclaration est:

@property(nonatomic, assign) id<UITableViewDelegate> delegate 

Si vous faites votre délégué comme ceci vous n'aurez pas le problème. Et il devrait être sûr puisque le contrôleur de vue parent est seulement aussi longtemps que la vue de l'enfant - oui?

+0

Je pense que vous avez raison. Je me bats pour penser à un exemple où j'utilise un objet délégué à plusieurs endroits ou le délégué survit à son parent mais je ne peux pas penser à un. Et si cela arrive, c'est probablement un mauvais design. Mon problème est que je n'étais pas toujours cohérent en faisant des délégués. Parfois, j'ai des contrôleurs avec des références les uns aux autres afin qu'ils puissent partager des données. Il n'est pas toujours clair de savoir qui devrait tenir le pointeur compté de référence dans cette situation. Je suppose que je devrais régler ça. – Mike

+0

@Mike, la convention est que l'objet parent contient toujours un pointeur "assign" à l'objet enfant. Tant que l'enfant sera libéré avant le parent, vous allez bien. – Bill

Questions connexes