KVO von einer ganz allgemeinen Veränderung möglich?

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • KVO von einer ganz allgemeinen Veränderung möglich?

    Hallo wieder,
    diesmal beschäftigt mich die Frage, ob man per KVO auch überwachen kann, ob sich ein Objekt ganz grundsätzlich verändert hat. In meinem Fall habe ich einen NSArrayController gesubclassed (was für ein Deutsch) der Entry Objekte verwaltet. Dieses Objekt werwaltet Einträge, die in ein paar NSTextFields gemacht werden. Welches wie verändert wird, spielt keine Rolle, es interesiert nur, dass überhaupt etwas verändert wurde (ähnlich wie TextEdit mitbekommt, dass ein Dokument sich ändert und das Icon eingraut). Delegates finde ich in dem Sinne unpraktisch, als das ich dann alle NSTextFields daran anschließen müsste. Außerdem soll nur das Endergebnis verglichen werden und nicht eventuell gemachte und wieder Rückgängig gemachte Einträge beachtet werden.
    Also KVO ist mir eingefallen, gibts sonst noch gute Möglichkeiten?
    Danke übrigens im Voraus schon :)
  • RE: KVO von einer ganz allgemeinen Veränderung möglich?

    Du musst dich naturgemäß schon an allle Objekte hängen. Oder du leitest den Controller ab und sorgst dafür, dass alle Änderungen über ihn laufen, was nicht ganz simpel ist.

    Du musst aber nicht alle Schüssel beobachten. Bastele in die enthaltenen Objekte einfach einen Schlüssel "change" ohne Bedeutung (Zufallszahlen). Dann machst du einen halb-autmatischen oder manuellen KVO auf den Schlüssel. Das habe ich hier beschrieben:

    macentwicklerwelt.net/index.ph…e_Observing#Manuelles_KVO

    Müsste gehen.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • hm, schade. vielleicht müsste ich mal sowas wie einen checksummenberechnung für Objekte erfinden. Das wäre sicherlich die beste Lösung. Deim debuggen kann man sich ja die aktuellen Werte hexadezimal ansehen.
    Aber deine Idee ist auch nicht schlecht.
  • Also zum einen gibt es so etwas und nennt sich Hash-Value.

    Aber das ist doch gleichgültig! Welchen Wert du im Schlüssel versteckst, spielt doch gar keine Rolle. Meinetwegen gibt immer 7.9 als float zurück. Es kommt doch nur auf die Notification an.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Wieso, das ist doch genau das was Sinn macht für meine Bedürfnisse. Ich habe meine Textfelder so eingestellt, dass bei erfolgloser Suche nach einem Entry ein neues Entry Objekt erstellt wird. Werden diese aber nicht ausgefüllt, z.B. weil der Benutzer nur nachschauen wollte, ob es das gibt oder evtl. sich vertippt hat, dann kann es wieder gelöscht werden. D.h. bei jedem select wechsel soll geprüft werden, ob der Wert sich verändert hat oder nicht. Dafür brauche ich keine ständige Überwachung.
  • ? Ich verstehe dein Problem immer noch nicht. Der "abgefragte" Wert muss sich doch gar nicht ändern. Die Change-Notification wird auch ausgelöst, wenn sich nichts ändert, oder etwa nicht?

    Mach einfach bei jeder Änderung ein

    Quellcode

    1. [self willChangeValueForKey:@"anyChange"];
    2. [...]
    3. [self didChangeValueForKey:@"anyChange"];


    Oder gleich ein halb-automatisches KVO mittels

    Quellcode

    1. [self setKeys:keyArrayWithAllProperties triggerChangeNotificationForDependentKey:@"anyChange"];


    Was für einen Wert anyChange hat, ist doch gleichgültig.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • ich glaube wir haben mehr oder weniger aneinander vorbei geschrieben. War aber nicht schlimm, weil deine Idee mich auf eine bessere gebracht hat.
    So hab ichs nun gelöst:
    Das komplette Objekt zu vergleichen ist blöd, weil mir das bald eher im Weg gewesen wäre, also habe ich eine Methode - (NSNumber *)checksum in mein Objekt eingebaut, das mir von allen Instanzen die ich brauche einen Hashwert liefert und den zusammenaddiert zurückgibt als NSNumber. Den kann man schön vergleichen und hat auch gleichzeitig den Vorteil, dass wenn ein String verändert wird, aber durch ein Undo wieder in den vorigen Zustand gebracht wird keinen veränderten HAshwert zurückgibt. Tja, so einfach wars dann schon :)
  • Hallo,

    also so ganz intuitiv finde ich die KVO Veranstaltung ja nicht, insb. da sie ja offenbar so nicht einmal vollständig implementiert ist ("insert", "change" etc werden auch bei mir nicht gesenden).

    Also nochmal für doofe, wie mich:

    Ich will erstmal wissen, ob sich die arrangedObjects in meiner Subclass von NSArrayController geändert haben. OK, so eine "Jo jo, irgendwas wird sich wohl schon geändert haben" -Change Notification bekomme ich, aber ist das wirklich schon alles? (Im change Dictionary ist "new" nil, und "old" auch).

    Jetzt interessiert mich ausserdem ob ein ganz bestimmter Key in den Objekten geändert wurde. Ah, das interessiert aber nur den Controleller selbst, also werde ich da doch mal geschickt ein paar Methoden überschreiben. Das müsste ja rauszubekommen sein, ohne das ich mich für jedes Object als Observer registriere. Oder ist das echt notwendig? Naja, OK, könnte ich dann in z.B. addObject machen :(

    Gruss

    Alex
    The only thing that really worried me was the ether.
  • Na, das ist eigentlich kein Fehler des KVO, sondern einer des NSArrayControllers, der sich offenbar zu fein dafür ist. ;)

    Du solltest das in eigenen Collections freilich machen.

    Wenn du alllgemein eine Änderung wissen willst und ganz speziell, dann wirst du wohl nicht drumherum kommen, einen allgemeinen und spezielle Observer zu implementieren. Mir scheint das eine Frage der Logik zu sein. Was du willst, ist praktisch einen Observer für bestimmte Eigenschaften und einen für alle Eigenschaften. Das ist ja auch kein Problem. Dann registriere eben den Observer für ddie bestimmten Eigenschaften und für die "alle-Eigenschaft" Das mag dir komisch erscheinen, ist aber das System. Du kannst freilich als Alternative dir einen weiteren Schlüssel "bestimmte Eigenschaften" anlegen, den du ersatzweise observierst und mittels halb-manuellem KVO setzt. Das wäre aber ein Verstoß gegen MVC, für den du dir einfach überlegen musst, ob du ihn in Kauf nimmst. Ob das dan schneller ist, weiß ich nicht.

    BTW: Bei einer Änderung werden ja nur die Observer für den einen Schlüssel gesucht. Demzufolge wird nichts langsamer, wenn du für andere Schlüssel auch noch Observer angemeldet hast. Langsam wird das ganze bei vielen Observern, nicht bei vielen Schlüsseln. So ist jedenfalls meine Erfahrung.

    Wenn du allerdings viele Observer hast, dann würde auch ein hand-made Controller (oder mehrere) ganz schön daran brutzeln, die Mitteilungen an die Views zu verteilen. Denn das _muss_ ja geschehen, weil es die Anforderung des Programmes ist. Egal ob du KVo nimmst oder nicht: Wenn viele Observieren, dann observieren viele. Oder wie würdest du das implementieren?
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Nochmal, ganz langsam:

    Meinst Du, sich soll meine eigene Change Notification für die Arranged Objects schicken? Da interessiert mich (also meinen View) in erster Linie, ob Objekte neu dazugekommen sind oder gelöscht wurden.

    Beim zweiten Punkt erkläre ich vielleicht mal, was ich will. Dann gibt's vielleicht auch ein paar coole Ideen:

    Die Daten in meinem NSArrayController sollen persistent sein, d.h. sie sollen gespeichert werden. Allerdings nur Daten, die auf eine bestimmte Art "valide" sind. Das klingt spannender als es ist, das kann man sich auch als einen Schlüssel "Speicher mich" vorstellen.

    Eine Idee war, die NSContoller subclass mach das vollkommen autark. Wenn bei einem Objekt der "Speicher mich" Schlüssel geändert wird, dann wird es gespeichert, oder evtl. wieder gelöscht.

    Die andere Idee ist aber, das macht jemand anders: Der liest den Content einfach ein, setzt ihn, und bevor mein Programm beendet wird, schreibt er den Content wieder weg.

    In beiden Fällen wäre es sicher sinnvoll, das der Content NSCoding compliant ist und sich selber (unter Beachtung des "Speicher mich" Schlüssels) selbst wegschreibt.

    (Der Benutzer kann einstellen, ob gepeichert werden soll, und auch wohin, danach soll es aber automatisch laufen).

    Noch Ideen?

    Danke

    Alex
    The only thing that really worried me was the ether.
  • Ich weiß auch nicht, ob wir aneinander vorbeireden. Also, wie ich es verstanden habe und lösen möchte:

    Du hast einen Container (Array), der Objekte enthält. Üblicherweise will man dann Änderungen des Container (Hinzufügen und Löschen von enthaltenen Objekten) und Änderungen der enthaltenen Objekte observieren. Man hat also n+1 Observierungen, für einen Container mit n Objekten. Beispiel für einen Container mit 2 Objekten:

    Container wird observiert wegen Einfügungen pp.
    Objekt1 wird observiert wegen Inhaltsänderungen
    Objekt2 wird observiert wegen Inhaltsänderungen

    Wenn du mehrere Schlüssel der Inhaltsobjekte observieren möchtest, so vervielfältigt sich das. Nehmen wir an: title und color

    Container wird observiert wegen Einfügungen pp.
    Objekt1 wird observiert wegen title
    Objekt1 wird observiert wegen color
    Objekt2 wird observiert wegen title
    Objekt2 wird observiert wegen color

    Du meintest jetzt, dass du eigentlich von den Objekten nicht spezielle Schlüssel (title, color) observieren möchtest, sondern einfach nur einen Summenschlüssel, damit du irgendeine Veränderung mitbekommst. Dazu meine Lösung:

    Du legst dir im Objekt einen neuen Schlüssel irgendeineÄnderung an (Möglicherweise hat dich mein Schlüsselname anyChange verwirrt, weil der auch beschisssen gewählt war) und observierst nur den:

    Container wird observiert wegen Einfügungen pp.
    Objekt1 wird observiert wegen irgendeineÄnderung
    Objekt2 wird observiert wegen irgendeineÄnderung

    Jetzt musst du natürlich bei jeder Änderung eines Schlüssels im Objekt (title, color, ...) auch eine Notification auf den Schlüssel irgendeineÄnderung triggern. Dazu meldest du das als halb-manuelles KVO an und schreibst dir eine Getter-Methode -(BOOL)irgendeineÄnderung{ return TRUE; }, welches einen Wert zurückgibt. Der Wert interessiert dich aber gar nicht, da du aber einer entsprechenden Change-Notification mit dem Schlüssel weißt: "Ah,. es hat sich irgendetwas im Objekt verändert", gleichgültig welcher Wert da kommt.

    Mit der Speicherlogik im Controller finde ich keine gute Idee. Zwei Gründe:
    1. Das verstößt gegen MVC. Das Model sollte sich selbst persistieren können.
    2. Du hast ja möglicherweise mehrere Controller auf dein Model. Welcher kümmert sich um das persistieren? Arbeiten alle gut zusammen?
    3. 1 und 2 lassen mich daran denken, dass es sich um eine Eigenschaft des Models handelt, nicht des Controllers. Es gibt freilich Fälle, in denen man auch Controller und Views persistiert. Deiner scheint mir aber keiner davon zu sein.

    Letztlich hängt das alles natürlich ganz stark davon ab, was du machst. Übrigens scheint deine SW eine ähnliche Struktur zu haben wie meine.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • 1. Das verstößt gegen MVC. Das Model sollte sich selbst persistieren können.


    OK, aber was heisst das denn? Natürlich soll jedes Model sich selbst speichern können. Aber irgendjemand muss dieses Speichern ja "triggern" und kontrollieren. Ich halte es für durchaus nicht falsch, wenn ein Model Dinge wie den Dateinamen nicht weiss.

    Ich werde den Content wohl vor dem Anlegen des Controllers sich selbst einlesen lassen, und beim beenden des Programms wieder wegschreiben. Vielleicht kommt dann noch mal eine "Speichern" Funktion irgendwo hin

    Gruss

    Alex
    The only thing that really worried me was the ether.
  • Selbstverständlich muss der Controller das "triggern" Das ist seine Aufgabe. Aber es stellt sich die Frage, ob er das ins "Model hinein" kontrollieren muss. Immerhin exportierst du damit die Model-Struktur.

    Hmm, ich stelle mir meistens folgende Kontrollfrage:

    Wenn ich zwei Controller auf das Model hätte, könnt dann die Eigenschaft sinnvoll verschieden sein?

    Wenn ich das mit nein beantworte, ist es eine zentrale Eigenschaft des Models. Dieses sollte sich also darum kümmern.

    Wenn ich die Frage mit ja beantworte, so handelt es sich poffenbar nicht um eine Eigenschaft des Models, sondern des Nutzers: Die Eigenschaft landet im Controller oder im View.

    Das sind natürlich nur Daumenregeln. Die Welt der realen Implementierungen hängt von vielem ab. Aber als Kontrollfrage tut es gute Dienste.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • ich poste mal meine Lösung von oben:

    Quellcode

    1. - (void)search:(id)sender
    2. {
    3. id temporaryObject = [self newObject];
    4. NSLog(@"selection checksum: %i",[[[self selection] valueForKeyPath:@"checksum"]intValue]);
    5. NSLog(@"temporaryObject checksum: %i",[[temporaryObject valueForKeyPath:@"checksum"]intValue]);
    6. if ([[[self selection] valueForKeyPath:@"checksum"] isEqualToNumber:[temporaryObject valueForKeyPath:@"checksum"]])
    7. {
    8. [self removeObjectAtArrangedObjectIndex:[self selectionIndex]];
    9. NSLog(@"temporaryObject removed");
    10. }
    11. [self setSearchString:[sender stringValue]];
    12. NSString *lowerSearch = [searchString lowercaseString];
    13. NSEnumerator *oEnum = [[self arrangedObjects]objectEnumerator];
    14. id item;
    15. int selection;
    16. selection = -1;
    17. while (item = [oEnum nextObject])
    18. {
    19. NSString *lowerName = [[item valueForKeyPath:@"uniqueID"] lowercaseString];
    20. NSLog(@"uniqueID = %@",[item valueForKeyPath:@"uniqueID"]);
    21. if ([lowerName isEqualToString:lowerSearch])
    22. {
    23. selection = [[self arrangedObjects] indexOfObject:item];
    24. [self setSelectionIndex:selection];
    25. NSLog(@"searchString found");
    26. NSLog(@"[[self arrangedObjects] indexOfObject:item] = %i",[[self arrangedObjects] indexOfObject:item]);
    27. break;
    28. }
    29. }
    30. if (selection == -1)
    31. {
    32. [temporaryObject setValue:[sender stringValue] forKey:@"uniqueID"];
    33. [self addObject:temporaryObject];
    34. [self setSelectionIndex:[[self arrangedObjects] indexOfObject:temporaryObject]];
    35. NSLog(@"temporaryObject added");
    36. }
    37. [sender selectText: 0];
    38. [[[sender window] fieldEditor:YES forObject: sender]setSelectedRange: NSMakeRange([[sender stringValue]length], 0)];
    39. }
    40. - (id)newObject
    41. {
    42. newObj = [super newObject];
    43. [newObj setValue:@"" forKey:@"uniqueID"];
    44. [newObj setValue:@"" forKey:@"verbTrans"];
    45. return newObj;
    46. }
    47. // - dealloc:
    48. - (void)dealloc
    49. {
    50. [self setSearchString: nil];
    51. [super dealloc];
    52. }
    53. // - searchString:
    54. - (NSString *)searchString
    55. {
    56. return searchString;
    57. }
    58. // - setSearchString:
    59. - (void)setSearchString:(NSString *)newSearchString
    60. {
    61. if (searchString != newSearchString)
    62. {
    63. [searchString autorelease];
    64. searchString = [newSearchString copy];
    65. }
    66. }
    Alles anzeigen