web-dev-qa-db-fra.com

L'instance a été désallouée alors que des observateurs clés étaient toujours enregistrés auprès de l'instance.

J'ai un UITableView.

Ici, j'ai des cellules différentes. Chaque cellule a un modèle. Avec KVO et NotificationCenter, la cellule écoute les modifications apportées au modèle. Lorsque je quitte ViewController, le message d'erreur suivant s'affiche:

An instance 0x109564200 of class Model was deallocated while key value observers were still registered with it. 
Observation info was leaked, and may even become mistakenly attached to some other object. 
Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0x109429cc0> (
<NSKeyValueObservance 0x109429c50: Observer: 0x10942d1c0, Key path: name, Options: <New: NO, Old: NO, Prior: NO> Context: 0x0, Property: 0x10968fa00>
)

Dans la cellule, je le fais lorsque la propriété de modèle est définie/modifiée:

[_model addObserver:self
         forKeyPath:@"name"
            options:0
            context:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(modelIsInvalid:)
                                             name:@"modelIsInvalid"
                                           object:_model];

Puis dans le dealloc de la cellule:

- (void)dealloc
{
    NSLog(@"DEALLOC CELL");
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [_model removeObserver:self forKeyPath:@"name"];
}

Dans le modèle, je vérifie également quand il est désalloué:

- (void)dealloc
{
    NSLog(@"DEALLOC MODEL");
}

Toutes les cellules sont désallouées avant tous les modèles, mais j'obtiens toujours cette erreur. De plus, je ne suis pas sûr de savoir comment définir le point d'arrêt mentionné dans l'erreur.

15
Johannes

Cela ne fonctionnera pas car les cellules sont réutilisées. Ainsi, lorsque la cellule quitte l'écran, elle n'est pas désallouée, elle est réutilisée.

Vous ne devriez pas enregistrer les notifications et KVO dans la cellule. Vous devez le faire dans le contrôleur d'affichage de table et lorsque le modèle change, vous devez mettre à jour le modèle et recharger les cellules visibles.

7
Greg

J'ai trouvé la réponse. Je ne peux pas supprimer le fil, quelqu'un a répondu :) Peut-être que ce sera utile pour quelqu'un.

Le problème est que UITableView va retirer de la file d'attente la même cellule que celle utilisée auparavant, pour une ligne plus longue (visible lorsque le défilement est suffisamment long).

Dans le setter j'ai maintenant:

// Before we set new model
if (_model) {
    [_model removeObserver:self forKeyPath:@"name"];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"modelIsInvalid" object:_model];
}

_model = model;

[_model addObserver:self
         forKeyPath:@"name"
            options:0
            context:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(modelIsInvalid:)
                                             name:@"modelIsInvalid"
                                           object:_model];
6
Johannes

En fonction de la réponse acceptée (ce qui est correct), vous pouvez la résoudre en supprimant l'observateur de la méthode " prepareForReuse " de la cellule.

Cette méthode sera appelée avant de réutiliser la cellule lors du défilement, etc., vous n'aurez donc aucun problème.

- (void)prepareForReuse{
    [_model removeObserver:self forKeyPath:@"name"];
}
4
Emilio

Il se peut que votre contrôleur de vue n’appelle pas la méthode dealloc, car sa référence pourrait être conservée par quelqu'un et que votre méthode dealloc ne soit pas appelée. Vous pouvez supprimer l’observateur avec votre méthode viewDidUnload: ou viewWillDisappear: ou vous pouvez suivre votre contrôleur dans l’instrument

0
Retro

Le meilleur endroit pour le faire pour les vues Cellules et Réutilisables est dans willMove(toSuperiew)

override func willMove(toSuperview newSuperview: UIView?) { if newSuperview == nil { // check for nil means this will be removed from superview self.collectionView?.removeObserver(self, forKeyPath: "contentSize") } }

0
Basheer_CAD