Eine Klasse hat eine Eigenschaft (und Instanzvariable) vom Typ NSMutableArray mit synthetisierten Accessoren (via @property
). Wenn Sie dieses Array mit folgenden Elementen beobachten:
[myObj addObserver:self forKeyPath:@"theArray" options:0 context:NULL];
Fügen Sie dann ein Objekt wie folgt in das Array ein:
[myObj.theArray addObject:NSString.string];
Eine ObservValueForKeyPath ... -Nachricht wird nicht gesendet. Folgendes sendet jedoch die richtige Benachrichtigung:
[[myObj mutableArrayValueForKey:@"theArray"] addObject:NSString.string];
Dies liegt daran, dass mutableArrayValueForKey
ein Proxy-Objekt zurückgegeben wird, das sich um die Benachrichtigung von Beobachtern kümmert.
Aber sollten die synthetisierten Accessoren ein solches Proxy-Objekt nicht automatisch zurückgeben? Wie kann ich das umgehen [super mutableArrayValueForKey...]
? Soll ich einen benutzerdefinierten Accessor schreiben, der nur aufgerufen wird ?
quelle
Ich würde nicht verwenden
willChangeValueForKey
unddidChangeValueForKey
in dieser Situation. Zum einen sollen sie anzeigen, dass sich der Wert an diesem Pfad geändert hat, nicht, dass sich die Werte in einer zu vielen Beziehung ändern. Sie möchtenwillChange:valuesAtIndexes:forKey:
stattdessen verwenden, wenn Sie dies auf diese Weise tun. Trotzdem ist die Verwendung solcher manueller KVO-Benachrichtigungen eine schlechte Kapselung. Eine bessere Möglichkeit besteht darin, eine MethodeaddSomeObject:
in der Klasse zu definieren, die das Array tatsächlich besitzt, einschließlich der manuellen KVO-Benachrichtigungen. Auf diese Weise müssen sich externe Methoden, die Objekte zum Array hinzufügen, nicht um die Behandlung der KVO des Array-Besitzers kümmern. Dies wäre nicht sehr intuitiv und könnte zu nicht benötigtem Code und möglicherweise zu Fehlern führen, wenn Sie Objekte zum Array hinzufügen Array von mehreren Orten.In diesem Beispiel würde ich tatsächlich weiter verwenden
mutableArrayValueForKey:
. Ich bin nicht positiv mit änderbaren Arrays, aber ich glaube , in die Dokumentation zu lesen , dass diese Methode tatsächlich das gesamte Array mit einem neuen Objekt ersetzt, so dass , wenn die Leistung ist ein Anliegen Sie auch umsetzen werden wolleninsertObject:in<Key>AtIndex:
undremoveObjectFrom<Key>AtIndex:
in der Klasse, die das Array besitzt .quelle
Wenn Sie nur die geänderte Anzahl beobachten möchten, können Sie einen aggregierten Schlüsselpfad verwenden:
[myObj addObserver:self forKeyPath:@"theArray.@count" options:0 context:NULL];
Beachten Sie jedoch, dass eine Neuordnung im Array nicht ausgelöst wird.
quelle
Ihre eigene Antwort auf Ihre eigene Frage ist fast richtig. Verkaufen Sie nicht
theArray
extern. Deklarieren Sie stattdessen eine andere Eigenschaft,theMutableArray
die keiner Instanzvariablen entspricht, und schreiben Sie diesen Accessor:- (NSMutableArray*) theMutableArray { return [self mutableArrayValueForKey:@"theArray"]; }
Das Ergebnis ist, dass andere Objekte
thisObject.theMutableArray
Änderungen am Array vornehmen können und diese Änderungen KVO auslösen.Die anderen Antworten weisen darauf hin, dass die Effizienz gesteigert wird, wenn Sie auch implementieren
insertObject:inTheArrayAtIndex:
undremoveObjectFromTheArrayAtIndex:
immer noch korrekt sind. Es ist jedoch nicht erforderlich, dass andere Objekte über diese Bescheid wissen oder sie direkt aufrufen müssen.quelle
theMutableArray
als wäre es eineNSMutableArray
Eigenschaft.Wenn Sie den Setter nicht benötigen, können Sie auch das einfachere Formular unten verwenden, das eine ähnliche Leistung (gleiche Wachstumsrate in meinen Tests) und weniger Boilerplate aufweist.
// Interface @property (nonatomic, strong, readonly) NSMutableArray *items; // Implementation @synthesize items = _items; - (NSMutableArray *)items { return [self mutableArrayValueForKey:@"items"]; } // Somewhere else [myObject.items insertObject:@"test"]; // Will result in KVO notifications for key "items"
Dies funktioniert, da, wenn die Array-Accessoren nicht implementiert sind und kein Setter für den Schlüssel vorhanden ist,
mutableArrayValueForKey:
nach einer Instanzvariablen mit dem Namen_<key>
oder gesucht wird<key>
. Wenn eine gefunden wird, leitet der Proxy alle Nachrichten an dieses Objekt weiter.Siehe diese Apple-Dokumente , Abschnitt "Accessor-Suchmuster für bestellte Sammlungen", Nr. 3.
quelle
readonly
dies sonst in einer Endlosschleife stecken bleibt ...Sie benötigen wickeln
addObject:
AnrufwillChangeValueForKey:
unddidChangeValueForKey:
Anrufe. Soweit ich weiß, gibt es für das NSMutableArray, das Sie ändern, keine Möglichkeit, über Beobachter Bescheid zu wissen, die seinen Besitzer beobachten.quelle
Eine Lösung besteht darin, ein NSArray zu verwenden und es durch Einfügen und Entfernen von Grund auf neu zu erstellen
- (void)addSomeObject:(id)object { self.myArray = [self.myArray arrayByAddingObject:object]; } - (void)removeSomeObject:(id)object { NSMutableArray * ma = [self.myArray mutableCopy]; [ma removeObject:object]; self.myArray = ma; }
als Sie bekommen die KVO und können alte und neue Array vergleichen
HINWEIS: self.myArray sollte nicht null sein, sonst wird arrayByAddingObject: auch null
Je nach Fall ist dies möglicherweise die Lösung. Da NSArray nur Zeiger speichert, ist dies kein großer Aufwand, es sei denn, Sie arbeiten mit großen Arrays und häufigen Vorgängen
quelle