KVO and array item observation

  • I have a bid revision object that holds a to-many NSSet reference to a
    group of items that constitute a tree. Some of those items are roots
    and have a nil parent. In my bid revision object I've set up a
    rootItems method that uses a predicate to return a filtered set of the
    items with nil parents and uses keyPathsForValuesAffectingRootItems to
    be aware of changes to the items set as a whole and generate
    appropriate KVO notifications.

    I have a problem though that when an item that's already in the set,
    but not a root, becomes a root by having its' parent set to nil. This
    does not cause KVO notifications to be sent out for the rootItems
    property because technically the items set as a whole has not changed.

    I naïvely tried to add @"items.parent" to the keyPathsForValues...
    method which of course didn't work since you can't observe groups of
    properties in a set or array. Right now, I'm sending a willChange/
    didChange method pair after performing parent changes of subitems and
    after undo/redo events and it *seems* to be working fine. This seems
    grotesque though and sure to cause me problems later.

    Is there any way to force a KVO update of a property besides a
    willChange/didChange method pair?
    Is there a better way?

    Ashley
  • On Oct 28, 2008, at 2:13 AM, Ashley Clark wrote:

    > I have a bid revision object that holds a to-many NSSet reference to
    > a group of items that constitute a tree. Some of those items are
    > roots and have a nil parent. In my bid revision object I've set up a
    > rootItems method that uses a predicate to return a filtered set of
    > the items with nil parents and uses
    > keyPathsForValuesAffectingRootItems to be aware of changes to the
    > items set as a whole and generate appropriate KVO notifications.
    >
    > I have a problem though that when an item that's already in the set,
    > but not a root, becomes a root by having its' parent set to nil.
    > This does not cause KVO notifications to be sent out for the
    > rootItems property because technically the items set as a whole has
    > not changed.
    >
    > I naïvely tried to add @"items.parent" to the keyPathsForValues...
    > method which of course didn't work since you can't observe groups of
    > properties in a set or array. Right now, I'm sending a willChange/
    > didChange method pair after performing parent changes of subitems
    > and after undo/redo events and it *seems* to be working fine. This
    > seems grotesque though and sure to cause me problems later.
    >
    > Is there any way to force a KVO update of a property besides a
    > willChange/didChange method pair?
    > Is there a better way?

    I've re-architected my design somewhat to work around the issue I
    described before and avoid issuing empty willChange/didChange pairs
    but I think I'm seeing a bug (in Apple's framework?) now that I'm not
    sure how to track.

    My object hierarchy looks like this:

    JTBidRevision maintains a set of JTBidItem objects that represent the
    top items of the tree.
    These JTBidItem objects in turn reference a JTInventory object.
    And these JTInventory objects each reference a set of
    JTInventoryDependency objects.

    My JTBidItem class has a method that returns a coalesced ordered array
    of objects that it displays as children, some of them are the
    JTInventoryDependency objects referenced through its JTInventory
    object and others are direct JTBidItem descendants of the node.

    The set of dependency objects returned also depends on the state of a
    variable in the parent JTBidRevision object. Depending on this
    variable some dependency objects will be or will not be part of the
    orderedChildren method result.

    I've set up my set of keyPaths that affect the orderedChildren set and
    it appears to be working in the general case in that if I change the
    watched value in my JTBidRevision instance the appropriate
    notifications are sent and the tree reloads correctly the first time
    for all elements. If I then change that value a second time, one of my
    objects has its orderedChildren method invoked twice while another of
    the objects gets skipped and the display of the tree is not updated
    correctly from that point on.

    The JTBidItem objects that are exhibiting this behavior both maintain
    a reference to the same JTInventory object.

    My keyPathsForValues... method looks like this for my JTBidItem's
    orderedChildren:

    + (NSSet *)keyPathsForValuesAffectingOrderedChildren {
        return [NSSet setWithObjects:@"children",
    @"inventoryItem.dependentInventoryItems", @"owningRevision.piping",
    nil];
    }

    The owningRevision itself is a derived value also with this
    keyPathsForValues..., the owningRevision method walks up the tree to
    find the owning revision object.

    + (NSSet *)keyPathsForValuesAffectingOwningRevision {
        return [NSSet setWithObjects:@"parent", @"bidRevision", nil];
    }

    Everything else appears to be working fine and the problem only occurs
    when two or more bid item objects in my tree both reference the same
    JTInventory object. Ideas?

    Ashley
  • I've made a test project that exhibits this behavior if anyone cares
    to look and filed a bug rdar://6327051

    http://idisk.mac.com/aclark78-Public/KVONotificationBug.zip

    Ashley

    On Oct 28, 2008, at 4:30 PM, Ashley Clark wrote:

    > On Oct 28, 2008, at 2:13 AM, Ashley Clark wrote:
    >
    >> I have a bid revision object that holds a to-many NSSet reference
    >> to a group of items that constitute a tree. Some of those items are
    >> roots and have a nil parent. In my bid revision object I've set up
    >> a rootItems method that uses a predicate to return a filtered set
    >> of the items with nil parents and uses
    >> keyPathsForValuesAffectingRootItems to be aware of changes to the
    >> items set as a whole and generate appropriate KVO notifications.
    >>
    >> I have a problem though that when an item that's already in the
    >> set, but not a root, becomes a root by having its' parent set to
    >> nil. This does not cause KVO notifications to be sent out for the
    >> rootItems property because technically the items set as a whole has
    >> not changed.
    >>
    >> I naïvely tried to add @"items.parent" to the keyPathsForValues...
    >> method which of course didn't work since you can't observe groups
    >> of properties in a set or array. Right now, I'm sending a
    >> willChange/didChange method pair after performing parent changes of
    >> subitems and after undo/redo events and it *seems* to be working
    >> fine. This seems grotesque though and sure to cause me problems
    >> later.
    >>
    >> Is there any way to force a KVO update of a property besides a
    >> willChange/didChange method pair?
    >> Is there a better way?
    >
    > I've re-architected my design somewhat to work around the issue I
    > described before and avoid issuing empty willChange/didChange pairs
    > but I think I'm seeing a bug (in Apple's framework?) now that I'm
    > not sure how to track.
    >
    >
    > My object hierarchy looks like this:
    >
    > JTBidRevision maintains a set of JTBidItem objects that represent
    > the top items of the tree.
    > These JTBidItem objects in turn reference a JTInventory object.
    > And these JTInventory objects each reference a set of
    > JTInventoryDependency objects.
    >
    > My JTBidItem class has a method that returns a coalesced ordered
    > array of objects that it displays as children, some of them are the
    > JTInventoryDependency objects referenced through its JTInventory
    > object and others are direct JTBidItem descendants of the node.
    >
    > The set of dependency objects returned also depends on the state of
    > a variable in the parent JTBidRevision object. Depending on this
    > variable some dependency objects will be or will not be part of the
    > orderedChildren method result.
    >
    >
    > I've set up my set of keyPaths that affect the orderedChildren set
    > and it appears to be working in the general case in that if I change
    > the watched value in my JTBidRevision instance the appropriate
    > notifications are sent and the tree reloads correctly the first time
    > for all elements. If I then change that value a second time, one of
    > my objects has its orderedChildren method invoked twice while
    > another of the objects gets skipped and the display of the tree is
    > not updated correctly from that point on.
    >
    > The JTBidItem objects that are exhibiting this behavior both
    > maintain a reference to the same JTInventory object.
    >
    >
    > My keyPathsForValues... method looks like this for my JTBidItem's
    > orderedChildren:
    >
    > + (NSSet *)keyPathsForValuesAffectingOrderedChildren {
    > return [NSSet setWithObjects:@"children",
    > @"inventoryItem.dependentInventoryItems", @"owningRevision.piping",
    > nil];
    > }
    >
    >
    > The owningRevision itself is a derived value also with this
    > keyPathsForValues..., the owningRevision method walks up the tree to
    > find the owning revision object.
    >
    > + (NSSet *)keyPathsForValuesAffectingOwningRevision {
    > return [NSSet setWithObjects:@"parent", @"bidRevision", nil];
    > }
    >
    >
    > Everything else appears to be working fine and the problem only
    > occurs when two or more bid item objects in my tree both reference
    > the same JTInventory object. Ideas?