NSKeyValueObservingOptionNew broken for NSArrayController: best practice workarounds?

  • I've seen many threads about the change dictionary in
    observeValueForKeyPath:ofObject:change:ontext: returning NULL values
    when NSKeyValueObservingOptionNew or NSKeyValueObservingOptionOld is
    passed to the addObserver:forKeyPath:options:context: of
    NSArrayController.

    But after looking at dozens (hundreds even) of messages all I can see
    is, "it's a known bug since way back in the Panther days", but not
    any workarounds.

    What's the best workaround for this, then?

    - subclass NSArrayController and override the KVO methods until I fix
    the breakage (not exactly sure how far I'd need to go with that)

    - in my observer keep a record of the "last known state" and then
    compare it against the new state whenever a notification is received
    (ie manually figure out what changed)

    - some sort of willChange/didChange trickery (did experiment with
    this but didn't cure the problem)

    Any ideas? I'm going to go with the "last known state" trick for now
    but if there's a better way I'd like to know about it. Thanks in
    advance.
  • On Oct 5, 2006, at 9:25 AM, Mailing list subscriptions wrote:

    > What's the best workaround for this, then?

    - avoid NSArrayController, if you *really* need to observe old and
    new states of the array (arrangedObjects, in my case)

    That's what I do.  My controller object is a subclass of
    NSObjectController (though I'm not sure that is even necessary,
    except for convenient 'content' initialization).  The 'content' is
    set to my main data object, which contains most of my important
    data.  This data object includes a fully KVO-compliant NSMutableArray
    to hold an important array of objects.

    I then manipulate the array directly through the KVO-compliant
    accessors.  Finally, other objects register as observers of the array
    directly and get full observing information of old and new states.

    This also means that my controller objects implements old fashioned
    data source routines for populating the NSTableView.

    I tried some of your other workarounds and just got frustrated, so I
    decided on this approach of avoiding NSArrayController all together.

    john

    Fight war, not wars.
  • El 5/10/2006, a las 18:25, Mailing list subscriptions escribió:

    > I've seen many threads about the change dictionary in
    > observeValueForKeyPath:ofObject:change:ontext: returning NULL
    > values when NSKeyValueObservingOptionNew or
    > NSKeyValueObservingOptionOld is passed to the
    > addObserver:forKeyPath:options:context: of NSArrayController.
    >
    > What's the best workaround for this, then?
    >
    > - subclass NSArrayController and override the KVO methods until I
    > fix the breakage (not exactly sure how far I'd need to go with that)
    >
    > - in my observer keep a record of the "last known state" and then
    > compare it against the new state whenever a notification is
    > received (ie manually figure out what changed)
    >
    > - some sort of willChange/didChange trickery (did experiment with
    > this but didn't cure the problem)

    Replying to my own post for the benefit of those searching the
    archives; there is a fourth option: totally refactoring your design.

    My code initially looked like this:

    NSUserDefaults (model) <-> NSUserDefaultsController <->
    NSArrayController <-> NSTableView

    The objects in NSUserDefaults were NSDictionaries in an array.
    Basically, I wanted to observe changes in that array, so from my main
    controller I registered as an observer. I chose this design because
    it was the "simplest" and required the smallest amount of glue code.

    I found that I was able to work around the
    NSKeyValueObservingOptionNew bug by restructuring things like this:

    NSUserDefaults (model) <-> Custom/main controller <->
    NSArrayController <-> NSTableView

    So basically I cut NSUserDefaultsController out of the loop and
    instead of storing an array of NSDictionaries in NSUserDefaults I now
    store an array of custom objects (requiring me to implement the
    NSCoding protocol in the custom class and write code using
    NSKeyedArchiver and NSKeyedUnarchiver in my main controller to handle
    the writing of the array to and from NSUserDefaults). I also had to
    write indexed accessor methods as a result of the move.

    By writing a custom class for the objects in the array, I no longer
    need to worry so much about things in the main controller because the
    objects themselves can now handle some of the work that the main
    controller would have done. In that sense the Cocoa bug has forced me
    to encapsulate things better and the design is now cleaner. On the
    down side, I had to write methods in the main controller for adding
    objects to/from the array, because some special behaviour was needed
    to set up the objects.

    Even though Cocoa Bindings is supposed to cut out a lot of glue code,
    there's a hell of a lot of glue in there after these changes, but it
    seems that the design is basically "correct" and doesn't break any
    Cocoa Bindings "best practice" guidelines. Here are some source code
    line counts:

    - NSIndexSet enumeration support: 201 lines
    - Drag and drop support category: 38 lines
    - Custom array controller (drag and drop aware): 124 lines
    - Custom table view (delete-key support): 97 lines
    - Custom model class: 134 lines
    - Main controller: 263
    - Value transformers: 69 lines
previous month october 2006 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
Go to today