NSButton, NSUserDefaults und KVO

  • NSButton, NSUserDefaults und KVO

    Ich stolpere gerade über ein Problem, dass möglicherweise bei mir erst seit der Tiger-Installation auftritt:
    Per Bindings werden diverse Programmeinstellungen über NSButtons (Checkbox-Style) gesetzt. Einige Objekte registrieren sich bei den NSUserDefaults, um Änderungen an diesen bool'schen Einstellungen mitzubekommen:

    Quellcode

    1. [[NSUserDefaults standardUserDefaults] addObserver: self
    2. forKeyPath: <der Einstellungsname>
    3. options: NSKeyValueObservingOptionNew
    4. context: nil];

    Merkwürdigerweise werden deren observeValueForKeyPath:ofObject:change:context:-Methoden nur beim Setzen des Häkchen aufgerufen, aber nicht beim Entfernen (also beim erneuten Anklicken).

    Könnte es sein, das sich da was unter 10.4 geändert hat? Ich habe den Code das letzte Mal unter 10.3 getestet und da lief es noch - was natürlich kein Nachweis ist, das der Fehler an 10.4 liegt.

    Hat jemand hier einen Tip auf Lager?

    Danke,
    Tjark
  • Ja, ist schon merkwürdig. In einem kleinem Testprojekt läuft alles.
    Merkwürdig ist, dass die Objekte für die eine Einstellung nur beim Setzen des Häkchen (1) und bei einer anderen Einstellung nur beim Entfernen des Häkchen (0) benachrichtigt werden. Ich frage mich, was sich hier verklemmt hat (irgendwelche Seiteneffekte)?
    Solche Fehler sind ja meistens schwer und manchmal nur mit Glück zu finden.
  • Die Lösung ist, dass alle Objekte, die über Bindings Änderungen an den Defaults mitbekommen wollen, sich bei dem "shared defaults controller" ([NSUserDefaultsController sharedUserDefaultsController]) anmelden müssen, und nicht bei den "user defaults" ([NSUserDefaults standardUserDefaults]).

    Als zweites ist wichtig, sich nicht bei dem Objekt anzumelden, dass mittels

    Quellcode

    1. [[NSUserDefaultsController sharedUserDefaultsController] values]
    zurückgegeben wird, sondern den key path selber zusammenzubauen und sich dann bei dem "shared defaults controller" anzumelden:

    Quellcode

    1. NSUserDefaultsController *sharedDefsController = [NSUserDefaultsController sharedUserDefaultsController];
    2. [sharedDefsController addObserver: self
    3. forKeyPath: [@"values." stringByAppendingString: <Wertname>]
    4. options: 0
    5. context: <Context>];

    (Beim Abmelden muss natürlich entsprechend verfahren werden.)

    In der observeValueForKeyPath:ofObject:change:context:-Implementierung kann dann aber der aktuelle Wert der Eigenschaft von [NSUserDefaults standardUserDefaults] erfragt werden; dies muss nicht über NSUserDefaultsController geschehen (zumindest, wenn "setAppliesImmediately" von NSUserDefaultsController auf YES gesetzt ist, was die Standardeinstellung ist).

    Wenn also z.B. eine Checkbox mittels Bindings über den "shared defaults controller" mit den Programmeinstellungen verbunden ist, werden die Werte sofort beim Anklicken der Checkbox in die NSUserDefaults geschrieben, aber (in einigen Fällen) so, dass Observer des NSUserDefaults nicht umbedingt Bescheid gesagt bekommen; das geht nur über den NSUserDefaultsController.

    Lange Rede, kurzer Sinn: Anmelden direkt bei [NSUserDefaultsController sharedUserDefaultsController], Werte können aber von [NSUserDefaults standardUserDefaults] wie gehabt abgefragt werden.
  • Ein anwelden am NSUserDefaults Objekt hab bei mir bis jetzt immer funktioniert. Seit heute nicht mehr. Komischerweise.
    Die Loesung, die Du gepostet hast funktioniert zwar, ist aber IMHO nicht im Sinne des Erfinders.
    Weil mit -addObserver melde ich mich ja fuer die Observierung einer Instanzvariable an. Wenn ich mich jetzt an dem UserDefaultsController anmelde und gebe bei Options an, dass ich den neuen Wert haben will, bekomme ich den in observeValueForKeyPath:ofObject:change:context: aber nicht mitgeliefert. Und das soll ja eigentlich der Sinn des ganzen sein.
    Man muss also den neuen Wert ueber NSUserDefaults erfragen.

    Manfred