Oooooohhhh! I see! Here’s the main problem:

> On Oct 14, 2016, at 23:08 , Gerriet M. Denkmann <g...@mdenkmann.de> 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 (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to