Maintaining mutable values with NSDictionaryControllerKeyValuePair

  • I'm using an NSDictionaryController object to control the data
    provided to an NSTableView object.  I'm attempting to use key-value
    coding through the dictionary controller to manipulate the data in a
    bindings-compatible way, by calling the method -arrangedObjects on the
    NSDictionaryController, and then manipulating the
    NSDictionaryControllerKeyValuePairs that this method returns in an
    array.

    The problem is that my values for the objects that -arrangedObjects
    returns also need to be mutable dictionaries themselves.  That is, my
    NSDictionaryControllerKeyValuePairs look like this:

    key:        someKey
    value:    NSMutableDictionary
      key: someKey1 value: someString1
      key: someKey2 value: someString2

    If I attempt to set the values within the NSMutableDictionary directly
    via key-value coding on a completely new object, by calling -
    setValue:forKeyPath:@"value.someKey1", I get errors saying that this
    class isn't KVC compliant for the key someKey1 (presumably because the
    KeyValuePair doesn't automatically know that I want to create an
    NSMutableDictionary as the value).

    So I'm left with manually creating the NSMutableDictionary value
    (which is fine), and then calling setValue: on the KeyValuePair.  The
    problem is, even if I use an NSMutableDictionary as the value, the
    KeyValuePair seems to set it with an immutable value instead
    (NSDictionary, rather than NSMutableDictionary).  I can see this
    because if I then call setValue:forKeyPath:@"value.someKey1", I get an
    error saying I'm sending a mutable method to an immutable object.  But
    I'm stuck, because I can't later set an entirely different
    NSMutableDictionary value to the KeyValuePair if I have my table
    observing someString1 or someString2, because that violates KVO.

    How do I get around this problem?  Is there a way to tell
    NSDictionaryControllerKeyValuePair to preserve the mutability of the
    values that I'm setting?  It seems that it might have internal copy
    semantics when setting the value rather than retain semantics, which
    would cause the problems I'm seeing.

    I hope that all made sense.  Any help would be greatly appreciated.
    Thanks!

    -- Simone Manganelli
  • > I'm using an NSDictionaryController object to control the data
    > provided to an NSTableView object.  I'm attempting to use key-value
    > coding through the dictionary controller to manipulate the data in a
    > bindings-compatible way, by calling the method -arrangedObjects on
    > the NSDictionaryController, and then manipulating the
    > NSDictionaryControllerKeyValuePairs that this method returns in an
    > array.

    Any luck?

    I am having a similar problem - mutable dictionary objects that are
    themselves stored in a dictionary will be presented as immutable
    dictionaries by the NSDictionaryController.  This makes it impossible
    to bind controls to keypaths that drill into dictionaries below the
    level of the outermost container.

    i.e. even if arrangedObjects.value should point to a mutable
    dictionary, attempts to edit arrangedObjects.value.someKey via a UI
    control will generate the following error:

      *** -[NSCFDictionary setObject:forKey:]: mutating method sent to
    immutable object

    Having a workaround for this would be really nice - has anyone found
    one?  I will file a bug as I don't think this is the behavior most
    would expect.

    This is turning something that would have "just worked" in about
    fifteen minutes into yet another quest for a messy workaround...

    Thanks in advance,

    Rick
  • Rick --

    I pulled my hair out over this one, but I finally found an acceptable
    workaround, although it doesn't solve the fundamental problem of
    NSDictionaryController.

    What I ended up doing is setting the whole outer dictionary (the one
    controlled by the NSDictionaryController) via a KVO-compliant method.
    This sucks because it means that you'll have to manually write methods
    for the actions of your UI controls instead of relying on bindings to
    do it for you.  But it *does* get around the paradox of either being
    unable to set properties of an individual object in the dictionary or
    violating KVO-compliance.

    So, for example, in my project, my NSDictionaryController is bound to
    the entriesDict of the selected EPWeblog object in my master-detail
    interface.  When I want to change one of the objects in the
    dictionary, I do the following:

    NSMutableDictionary *tempMutDict = [NSMutableDictionary
    dictionaryWithDictionary:[weblog entriesDict]];
    [tempMutDict setObject:dictionaryValue forKey:theExistingKey];
    [weblog setEntriesDict:tempMutDict];

    entriesDict is a property of the EPWeblog object (the variable
    'weblog'), and it's KVO-compliant via the entriesDict and
    setEntriesDict: methods.  So I simply copy the outer dictionary,
    change the one object in the dictionary (represented by the new
    dictionaryValue variable), and then set the outer dictionary via the
    setEntriesDict: method.  If you call addObject: to your
    NSDictionaryController, it does precisely the same thing  -- it adds a
    new object and sets the whole outer dictionary via the appropriate KVO-
    compliant method.

    After all this, I too am planning to file a bug on
    NSDictionaryController because this is pretty ridiculous for something
    that should be straightforward.  I did a bunch of subclassing to
    figure out exactly what NSDictionaryController was doing.  Even if the
    dictionary it's controlling contains mutable values inside its
    objects, NSDictionaryController represents them as immutable objects.
    It's infuriating.

    It does make me feel better that someone else is having this same
    problem, though. :)

    -- Simone

    Il giorno 2009-01-06, alle ore 14:06, Rick Hoge ha scritto:

    >
    >> I'm using an NSDictionaryController object to control the data
    >> provided to an NSTableView object.  I'm attempting to use key-value
    >> coding through the dictionary controller to manipulate the data in
    >> a bindings-compatible way, by calling the method -arrangedObjects
    >> on the NSDictionaryController, and then manipulating the
    >> NSDictionaryControllerKeyValuePairs that this method returns in an
    >> array.
    >
    > Any luck?
    >
    > I am having a similar problem - mutable dictionary objects that are
    > themselves stored in a dictionary will be presented as immutable
    > dictionaries by the NSDictionaryController.  This makes it
    > impossible to bind controls to keypaths that drill into dictionaries
    > below the level of the outermost container.
    >
    > i.e. even if arrangedObjects.value should point to a mutable
    > dictionary, attempts to edit arrangedObjects.value.someKey via a UI
    > control will generate the following error:
    >
    > *** -[NSCFDictionary setObject:forKey:]: mutating method sent to
    > immutable object
    >
    > Having a workaround for this would be really nice - has anyone found
    > one?  I will file a bug as I don't think this is the behavior most
    > would expect.
    >
    > This is turning something that would have "just worked" in about
    > fifteen minutes into yet another quest for a messy workaround...
    >
    > Thanks in advance,
    >
    > Rick
  • > I pulled my hair out over this one, but I finally found an
    > acceptable workaround, although it doesn't solve the fundamental
    > problem of NSDictionaryController.

    Thanks very much for the reply and workaround.  I will explore this
    solution further.

    The NSDictionaryController/bindings route seems to work fine if you
    have a simple mutable dictionary in which the keys are strings and the
    values are discrete objects like NSStrings or NSNumbers.  Then you can
    edit the values with no problem.  It just seems to get difficult when
    the dictionary values are themselves collections.  I could live with
    this I suppose, in cases where read-only display of the dictionary was
    the primary goal.  However it would really be nice to be able to edit
    the contents of collections, and the present limitations should really
    be mentioned in the documentation (maybe the current docs say so in
    some obtuse way, but if so I didn't catch it).

    Thanks again,

    Rick
  • >> I pulled my hair out over this one, but I finally found an
    >> acceptable workaround, although it doesn't solve the fundamental
    >> problem of NSDictionaryController.
    >
    > Thanks very much for the reply and workaround.  I will explore this
    > solution further.
    >
    > The NSDictionaryController/bindings route seems to work fine if you
    > have a simple mutable dictionary in which the keys are strings and
    > the values are discrete objects like NSStrings or NSNumbers.  Then
    > you can edit the values with no problem.  It just seems to get
    > difficult when the dictionary values are themselves collections.  I
    > could live with this I suppose, in cases where read-only display of
    > the dictionary was the primary goal.  However it would really be
    > nice to be able to edit the contents of collections, and the present
    > limitations should really be mentioned in the documentation (maybe
    > the current docs say so in some obtuse way, but if so I didn't catch
    > it).
    >

    After considering this problem some more, it seems like in my case the
    easiest workaround is to abandon the idea of having a dictionary of
    dictionaries and instead use an NSArray (or NSSet) of dictionaries.
    This will avoid the use of mysterious immutable proxy dictionaries by
    NSDictionaryController and should deliver equivalent functionality.
    Originally I was attracted by the simplicity of obtaining a list of
    entries using allKeys, and addressing the entries by name, but similar
    functionality could be obtained with other connection objects using
    predicates (with a bit more complexity).

    Would still be sweet if NSDictionaryController provided deeper read-
    write access to embedded dictionaries, but I won't hold my breath.

    Thanks again,

    Rick