Quand dois-je libérer des objets dans - (void) viewDidUnload plutôt que dans -dealloc?

103

À quoi sert-il -(void)viewDidUnload?

Ne pourrais-je pas simplement tout relâcher -dealloc? Si la vue se déchargeait, ne serait-elle pas -deallocappelée de toute façon?

Merci
la source

Réponses:

51

En plus de ce qui a déjà été indiqué, je voulais en dire plus sur la logique derrière -viewDidUnload.

L'une des raisons les plus importantes de sa mise en œuvre est que les UIViewControllersous - classes contiennent généralement également des références propriétaires à diverses sous-vues dans la hiérarchie de vues. Ces propriétés peuvent avoir été définies IBOutletslors du chargement à partir d'une pointe, ou par programmation à l'intérieur -loadView, par exemple.

La propriété supplémentaire des sous-vues par le UIViewControllerfait que même lorsque sa vue est supprimée de la hiérarchie de vues et libérée pour économiser de la mémoire, à travers laquelle les sous-vues sont également libérées par la vue, elles ne seront pas réellement désallouées car le UIViewControllerlui-même contient toujours son propre en suspens. en conservant également les références à ces objets. Libérer la UIViewControllerpropriété supplémentaire de ces objets garantit qu'ils seront également libérés pour libérer de la mémoire.

Les objets que vous libérez ici sont généralement recréés et définis à nouveau lorsque la UIViewControllervue est re-loaded, soit à partir d'un Nib, soit via une implémentation de -loadView.

Notez également que la UIViewController viewpropriété est nilau moment où cette méthode est appelée.

Sean Patrick Murphy
la source
1
Vous devriez lire developer.apple.com/library/ios/#featuredarticles/... pour comprendre le cycle de vie du contrôleur de vue / vue
Paul Solt
21

Comme le dit la documentation :

Il est appelé dans des conditions de mémoire insuffisante lorsque le contrôleur de vue doit libérer sa vue et tous les objets associés à cette vue pour libérer de la mémoire.

Dans la même situation nedealloc est pas appelé. Cette méthode n'est disponible que dans OS3 et supérieur. Faire face à la même situation dans iPhone OS 2.x était une vraie douleur!

Mise à jour de juillet 2015 : il convient de noter que viewDidUnloadcette méthode est obsolète dans iOS 6 car «Les vues ne sont plus purgées dans des conditions de mémoire insuffisante et cette méthode n'est donc jamais appelée». Ainsi, le conseil moderne est de ne pas s'en soucier et de l'utiliser dealloc.

Stephen Darlington
la source
6
Également à partir de la documentation: "Vous ne devez faire cela que pour les objets que vous pouvez facilement recréer ultérieurement, soit dans votre méthode viewDidLoad, soit à partir d'autres parties de votre application. Vous ne devez pas utiliser cette méthode pour libérer des données utilisateur ou toute autre information qui ne peut pas être facilement recréé ". C'est une question que j'avais moi-même, merci!
leolobato
Que faire si la vue est actuellement visible? Ne serait-il pas mauvais de le supprimer en raison d'un avertissement de mémoire insuffisante? ;) alors l'application serait juste vide vide. Je n'ai pas l'intention de libérer la vue à cause d'une mémoire insuffisante. Si je ne vois pas de vue, je libère toujours le contrôleur entier. Althogh j'ai un contrôleur de vue racine qui reste intact et gère tout le chargement / déchargement de ses contrôleurs de vue enfants ...
Merci
Non, vous ne l'utiliseriez pas si vous permutiez simplement une vue pour une autre. Pensez au cas où vous avez une "pile" de vues avec un UINavigationController. Une seule vue est visible et, si vous avez un avertissement de mémoire, vous pouvez libérer toutes celles qui ne sont pas visibles.
Stephen Darlington
Comment contrôlez-vous que viewDidUnload ne soit pas appelé sur la vue visible actuelle comme l'a noté Thanks?
arielcamus
1
viewDidUnload ne sera pas appelé sur la vue actuellement visible, uniquement sur les vues qui ne sont pas visibles.
programme
9

Ceci est dû au fait que vous définissez généralement @propertycomme "(nonatomic, retain)"et en tant que tel le setter qui est créé pour vous libère l'objet actuel et conserve ensuite l'argument ie

self.property = nil;

... fait quelque chose du genre:

[property release];
property = [nil retain];

Par conséquent, vous faites d'une pierre deux coups: la gestion de la mémoire (libérant l'objet existant) et l'affectation du pointeur à nil (car l'envoi de tout message à un pointeur nil renverra nil).

J'espère que cela pourra aider.

gazzaaa87
la source
8

N'oubliez pas qu'il viewDidUnloads'agit d'une méthode dans le contrôleur de vue, pas dans la vue. La méthode de la vue dealloc sera appelée lors du déchargement de la vue, mais la méthode du contrôleur de vue dealloc ne peut être appelée que plus tard.

Si vous recevez un avertissement de mémoire insuffisante et que votre vue ne s'affiche pas, ce qui se produira par exemple à chaque fois que vous utilisez un UIImagePickerController pour permettre à l'utilisateur de prendre une photo, votre vue sera déchargée et devra être rechargée après cela.

David Maymudes
la source
ça a du sens. Que se passe-t-il si je laisse toujours tomber le contrôleur de vue entier? C'est ce que je fais réellement. Dans ce cas, je n'ai pas beaucoup à faire avec -viewDidUnload, non? Je n'ai jamais eu la situation où je ne supprimerais que la vue, car je laisse toujours tomber le contrôleur entier s'il n'est de toute façon pas visible.
Merci le
eh bien, rappelez-vous simplement que dans le cas où votre vue est affichée, mais que vous avez une vue plein écran comme ImagePicker au-dessus, votre vue peut être déchargée même si vous ne l'aviez pas prévu.
David Maymudes
6

Conclusion:

Les contrôleurs de vue ont une propriété de vue. En règle générale, une pointe ou un morceau de code ajoute d'autres vues à cette vue. Cela se produit souvent dans une méthode -viewDidLoad, comme ceci:

- (void)viewDidLoad {
    [super viewDidLoad];
    [self createManyViewsAndAddThemToSelfDotView];
}

en outre, un fichier nib peut créer un bouton et l'ajouter à la vue du contrôleur de vue.

Sur iPhone OS 2.2, lorsque -didReceiveMemoryWarning était appelé depuis le système, vous deviez libérer quelque chose pour libérer de la mémoire. Vous pouvez libérer la vue entière du contrôleur de vue si cela a du sens. Ou tout simplement de gros contenus gourmands en mémoire.

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}

Maintenant, dans le nouvel OS 3.0, il existe une méthode -viewDidUnload, qui sera appelée à partir du système lorsque la vue a été déchargée en raison d'une mémoire insuffisante (veuillez me corriger: quand exactement cela est-il appelé?)

-viewDidUnload est utilisé pour libérer tous les objets appartenant à la fois au contrôleur de vue lui-même et à la vue. La raison: si un contrôleur de vue contient des références aux fils de la vue, c'est-à-dire à un bouton, les vues enfants référencées ne seront pas libérées, car leur nombre de rétention est> = 1. Une fois qu'elles sont libérées dans -viewDidUnload, elles peuvent être libérées de mémoire.

Merci
la source
1
rappel en vue a déchargé pour faire self.button = nil ;, pas [bouton release] ;.
mk12
6

ViewWillUnload est obsolète par Apple, vous devez maintenant utiliser didReceiveMemoryWarning ou dealloc pour libérer vos objets.

Dans iOS 6, les méthodes viewWillUnload et viewDidUnload de UIViewController sont désormais obsolètes. Si vous utilisiez ces méthodes pour libérer des données, utilisez plutôt la méthode didReceiveMemoryWarning. Vous pouvez également utiliser cette méthode pour libérer des références à la vue du contrôleur de vue si elle n'est pas utilisée. Vous devrez tester que la vue n'est pas dans une fenêtre avant de faire cela.

Guilherme Torres Castro
la source
5

Si le contrôleur de vue est sorti de la pile du contrôleur de navigation et n'est conservé nulle part ailleurs, il sera désalloué et dealloc sera appelé à la place de viewDidUnload. Vous devez libérer les vues créées dans loadView dans dealloc, mais il n'est pas nécessaire de définir les variables sur nil, car peu de temps après l'appel de dealloc, les variables n'existeront plus.

Colin
la source
3

Vous pouvez libérer toutes les sous-vues sur lesquelles vous vous tenez, par exemple cette UIImageView que vous avez conservée dans votre méthode loadView, ou mieux encore l'image qui se trouvait sur cette UIImageView.

Drvdijk
la source