74 votes

Observation d'un NSMutableArray pour insertion/suppression

Une classe a une propriété (et une var d'instance) de type NSMutableArray avec des accesseurs synthétisés (par l'intermédiaire de @property ). Si vous observez ce tableau en utilisant :

[myObj addObserver:self forKeyPath:@"theArray" options:0 context:NULL];

Et ensuite insérer un objet dans le tableau comme ceci :

[[myObj theArray] addObject:[NSString string]];

Une notification observeValueForKeyPath... est pas envoyé. Cependant, l'exemple suivant envoie la notification appropriée :

[[myObj mutableArrayValueForKey:@"theArray"] addObject:[NSString string]];

Cela s'explique par le fait que mutableArrayValueForKey renvoie un objet proxy qui se charge de notifier les observateurs.

Mais les accesseurs synthétisés ne devraient-ils pas retourner automatiquement un tel objet proxy ? Quelle est la meilleure façon de contourner ce problème ? Dois-je écrire un accesseur personnalisé qui invoque simplement l'option [super mutableArrayValueForKey...] ?

79voto

Peter Hosey Points 66275

Mais les accesseurs synthétisés ne devraient-ils pas retourner automatiquement un tel objet proxy ?

Non.

Quelle est la meilleure façon de contourner ce problème ? Dois-je écrire un accesseur personnalisé qui invoque simplement les éléments suivants [super mutableArrayValueForKey...] ?

Non. Mettez en œuvre la accesseurs de tableaux . Lorsque vous les appelez, la KVO affiche automatiquement les notifications appropriées. Donc tout ce que vous avez à faire est :

[myObject insertObject:newObject inTheArrayAtIndex:[myObject countTheArray]];

et la bonne chose se produira automatiquement.

Pour plus de commodité, vous pouvez écrire un addTheArrayObject: l'accesseur. Cet accesseur appelle l'un des accesseurs de tableaux réels décrits ci-dessus :

- (void) addTheArrayObject:(NSObject *) newObject {
    [self insertObject:newObject inTheArrayAtIndex:[self countTheArray]];
}

(Vous pouvez et devez indiquer la classe appropriée pour les objets dans le tableau, à la place de NSObject .)

Ensuite, au lieu de [myObject insertObject:…] vous écrivez [myObject addTheArrayObject:newObject] .

Tristement, add<Key>Object: et son homologue remove<Key>Object: ne sont, aux dernières nouvelles, reconnues par le KVO que pour les propriétés de type set (comme dans NSSet), et non pour les propriétés de type array, ce qui fait que vous n'obtiendrez pas de notifications gratuites du KVO avec elles, à moins que vous ne les implémentiez au-dessus d'accesseurs qu'il reconnaît. J'ai déposé un bug à ce sujet : x-radar://problem/6407437

J'ai une liste de tous les formats de sélecteurs d'accès sur mon blog.

9voto

Marc Charbonneau Points 30464

Je n'utiliserais pas willChangeValueForKey et didChangeValueForKey dans cette situation. D'une part, ils sont censés indiquer que la valeur de ce chemin a changé, et non que les valeurs dans une relation de type "to-many" changent. Il est préférable d'utiliser willChange:valuesAtIndexes:forKey: à la place, si vous le faites de cette façon. Malgré tout, l'utilisation de notifications KVO manuelles de cette manière constitue une mauvaise encapsulation. Une meilleure façon de procéder consiste à définir une méthode addSomeObject: dans la classe qui possède réellement le tableau, ce qui inclut les notifications manuelles de la KVO. De cette façon, les méthodes extérieures qui ajoutent des objets au tableau n'ont pas à s'inquiéter de gérer également le KVO du propriétaire du tableau, ce qui ne serait pas très intuitif et pourrait conduire à du code inutile et éventuellement à des bogues si vous commencez à ajouter des objets au tableau à partir de plusieurs endroits.

Dans cet exemple, je continuerais en fait à utiliser mutableArrayValueForKey: . Je ne suis pas sûr des tableaux mutables, mais je crois, en lisant la documentation, que cette méthode remplace en fait le tableau entier par un nouvel objet, donc si les performances sont un souci, vous voudrez également implémenter la méthode insertObject:in<Key>AtIndex: et removeObjectFrom<Key>AtIndex: dans la classe qui possède le tableau.

7voto

berbie Points 28

lorsque vous souhaitez simplement observer la modification du comptage, vous pouvez utiliser un chemin de clé agrégé :

[myObj addObserver:self forKeyPath:@"theArray.@count" options:0 context:NULL];

mais sachez que tout réordonnancement dans le tableau ne se fera pas.

3voto

matt Points 60113

Votre propre réponse à votre propre question est presque juste. Ne vendez pas theArray en externe. Au lieu de cela, déclarez une autre propriété, theMutableArray correspondant à aucune variable d'instance, et écrire cet accesseur :

- (NSMutableArray*) theMutableArray {
    return [self mutableArrayValueForKey:@"theArray"];
}

Le résultat est que d'autres objets peuvent utiliser thisObject.theMutableArray pour faire des changements dans le tableau, et ces changements déclenchent la KVO.

Les autres réponses soulignant que l'efficacité est accrue si vous mettez également en œuvre insertObject:inTheArrayAtIndex: et removeObjectFromTheArrayAtIndex: sont toujours correctes. Mais il n'est pas nécessaire que d'autres objets aient à les connaître ou à les appeler directement.

0voto

Ben Gottlieb Points 59900

Vous devez envelopper votre addObject: appeler willChangeValueForKey: et didChangeValueForKey: appels. Pour autant que je sache, il n'y a aucun moyen pour le NSMutableArray que vous modifiez de savoir si des observateurs surveillent son propriétaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X