Re: observeValueForKeyPath:... called too often in macOS 12
Re: observeValueForKeyPath:... called too often in macOS 12
- Subject: Re: observeValueForKeyPath:... called too often in macOS 12
- From: Quincey Morris <email@hidden>
- Date: Wed, 19 Oct 2016 00:51:24 -0700
- Feedback-id: 167118m:167118agrif8a:167118sPhMd7EMZy:SMTPCORP
Oooooohhhh! I see! Here’s the main problem:
> On Oct 14, 2016, at 23:08 , Gerriet M. Denkmann <email@hidden> wrote:
>
> My app (macOS 12) observes a value in NSUserDefaults.
Well, no, it doesn’t. It observes a value in a NSUserDefaultsController.values. That’s a different kettle of wax.
First of all, any NSController subclass defines a mediating controller, aka a glue object, aka a piece of junk black box. NSUserDefaultsController exists in particular to provide a way of binding UI elements directly to NSUserDefaults *without programmatic involvement*. As far as I’m concerned, any programmatic use of a NSController class is a code smell (although there are certainly a few things where the programmatic need is greater than the smell).
However, NSController subclasses do what they do, however they want to do it. The “new” 10.12 behavior you’re seeing may be a bug, or it may be a change to the implementation that doesn’t care about poorer performance, or … . See above under “piece of junk” and “black box”.
NSUserDefaultsController.values *is* documented to be KVO compliant, and you *can* observe its values, but there’s no API contract that says you’re going to be happy about what exactly happens.
Some comments about particular issues:
> Bug1: no NSKeyValueObservingOptionOld. Should have old value, but just has NSNull
I think all NSController subclasses have this flaw.
> Bug2: called at start (after applicationDidFinishLaunching) - although nothing has changed yet
The user defaults haven’t changed, but the internal binding[-equivalent] between NSUserDefaultsController and NSUserDefaults has to be set up, and that *is* a change to the values as held by the NSUserDefaultsController. If this is different from 10.11, it may actually be a bug fix.
> Bug3: any change is observed twice
Looking at the stack trace, the first notification occurs when the text field reports its new value across its binding to the NSUserDefaultsController. The second occurs sometime later. I suspect that’s because the change to the NSUserDefaults value flows back across the NSUserDefaultsController binding. This may be a bug, or it may be a result of some part of the NSUserDefaults happening asynchronous now. AFAIK, there’s no clear contract about values going around in circles across bindings. See above under “piece of junk” and “black box”.
> Bug4: changes not keep on quit
> Check: change a TextField; do not use CR or leave the TextField; quit the app. Start it again.
> Maybe this is the way it should be. But in the age of autoSave it feels a bit strange though.
We’re treating this as not a bug in observations.
> Bug5: changing a TextField to empty string reverts to registrationDefaults for this TextField.
> Check: start app; set both TextFields to other than registrationDefaults; quit.
> start app again; select any TextField; hit "delete", then CR or tab to other TextField →
> empty string will be replaced by the registrationDefault.
>
> This happens only once per TextField. Replacing the registrationDefault again with an empty
> string just keeps this empty string.
> At the next start of the app, the empty string will again be replaced by the registrationDefault.
>
> Maybe this is the way it should be: empty string → nil and nil → registrationDefault.
> Feels a bit strange though.
I think this is a consequence of using a binding or of using a NSController subclass. One of those mechanisms, I guess, changes the empty string to nil, being unaware that that’s going to trigger the reversion to registration defaults. It’s probably doing the right thing according to some set of rules, just not the rules you want.
> Bug6: sometimes the first change in any TextField reverts to registrationDefaults for both TextFields
> Check: not reproducible. Just happens when it feels like it: sometimes bug a few times in a row,
> sometimes no bug half a dozen times.
I dunno, I didn’t get as far as looking into this.
My advice is: don’t use NSUserDefaultsController for this. (Don’t use NSUserDefaultsController except for binding UI elements directly without the involvement of code.) Unfortunately NSUserDefaults isn’t documented as KVO compliant for user-defined keys, although it may actually be.
The way I handle this is that I create a custom class for preferences, which is fully KVO compliant for its custom properties, and I bind to or observe this object. Internally, the preferences object updates and/or reads the NSUserDefaults values. This is actually a useful level of abstraction, because often what you would like to store is not quite in the same form as what you would like to display. At the same time, it tends to be a lot of glue code.
There’s probably more to discuss, but this post already got long.
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden