Crash on NSKVOPendingNotificationRelease (KVO, bindings issue?)

  • I'm using KVO as a generalized recalculation engine, somewhat akin to how a spreadsheet recalcuates values based on what's changed. It's mostly successful once you eliminate cycles, but there is a persistent problem.

    On each relevant object, I have a observeValueForKeyPath:ofObject:change:context: method which then triggers willChangeValueForKey:  / didChangeValueForKey: for dependent properties. I've noticed that when observeValueForKeyPath:ofObject:change:context gets called more than once in a row for an object, one of the other observed objects dies in didChangeValueForKey: -- here's the stack trace

    Program received signal:  “EXC_BAD_ACCESS”.

    #0    0x7fff84d31ae1 in NSKVOPendingNotificationRelease
    #1    0x7fff88aa0bd3 in __CFArrayReleaseValues
    #2    0x7fff88a815f8 in _CFArrayReplaceValues
    #3    0x7fff84d31e35 in NSKeyValuePopPendingNotificationPerThread
    #4    0x7fff84d31dca in NSKeyValueDidChange
    #5    0x7fff84d1574f in -[NSObject(NSKeyValueObserverNotification) didChangeValueForKey:]
    #6    0x1000181c8 in -[CMEdge didChangeValueOnceForKey:] at CMEdge.m:133
    #7    0x100019f51 in -[CMNode observeValueForKeyPath:ofObject:change:context:] at CMNode.m:222

    The only workaround is to perform the didChangeValueForKey: in the next event cycle i.e. by using performSelectorOnMainThread:withObject:waitUntilDone:

    Not really ideal especially since I'm worried I'm just covering up the root of the problem, whatever it is.

    Any ideas on the cause?

    Side question: I can of course use keyPathsForValuesAffectingValueForKey: and friends to declare the dependencies instead of using a procedure to react to them, but I need to execute code as well e.g. update the observed value. For example, say A = B/C. When either B or C changes, you want A to change too. I can use keyPathsForValuesAffectingA to declare it depends on B or C, but I need to update the cached A value whenever B or C changes -- how would I do that without using observeValueForKeyPath:ofObject:change:context:?

    Cheers
    Glen Low
    Pixelglow Software
  • On Apr 21, 2010, at 17:47, Glen Low wrote:

    > I'm using KVO as a generalized recalculation engine, somewhat akin to how a spreadsheet recalcuates values based on what's changed. It's mostly successful once you eliminate cycles, but there is a persistent problem.

    The likely answer (see below for the reasons) is that this is probably not a good approach. Your application needs a specific behavior of change propagation, cycle elimination -- as well as other possible constraints -- that KVO has no API contract to provide. I'd recommend you spend the effort on designing your own object graph and related behavior.

    > On each relevant object, I have a observeValueForKeyPath:ofObject:change:context: method which then triggers willChangeValueForKey:  / didChangeValueForKey: for dependent properties. I've noticed that when observeValueForKeyPath:ofObject:change:context gets called more than once in a row for an object, one of the other observed objects dies in didChangeValueForKey: -- here's the stack trace

    I *believe* that KVO contains at least one (major? inherent?) bug that causes crashes like this with certain patterns of observations. Whenever I run into such crashes, I've never been able to work out whether it is in fact a bug, or if I did something wrong, nor to recreate the crash in a more controlled context that could be used as the basis of a bug report.

    So you might have run into a bug. Or you might have set up an observation pattern that KVO doesn't really support (either at the level of observers or the level of notifications).

    KVO has two important defects from the developer's point of view: it's a black box system, so trying to understand what it's doing at any given moment is pure guesswork; and it's an implementation-defined system, so that beyond its general responsibilities, what is does is what it's "supposed to" do, and there's no independent standard of whether it's right or wrong.

    Plus, KVO probably doesn't scale well as the number of observations increases. If your object graph is going to get large, performance may well become a consideration.

    For all those reasons, I wouldn't suggest using it for an application like this, where *propagation* of the notifications across multiple objects is the key functional issue.

    FWIW.

    > Side question: I can of course use keyPathsForValuesAffectingValueForKey: and friends to declare the dependencies instead of using a procedure to react to them, but I need to execute code as well e.g. update the observed value. For example, say A = B/C. When either B or C changes, you want A to change too. I can use keyPathsForValuesAffectingA to declare it depends on B or C, but I need to update the cached A value whenever B or C changes -- how would I do that without using observeValueForKeyPath:ofObject:change:context:?

    If you really need to "cache" the A value, then you have to use observeValueForKeyPath:ofObject:change:context:. Using keyPathsForValuesAffectingA only makes sense if you're going to produce the A value on demand.

    Incidentally, this example points another defect of using KVO for your application. You want A to change when B or C changes, and it will -- literally, meaning twice. Once for the B change, and once for the C change. That means that A may temporarily have an invalid value, and that might itself be propagated via notifications before its final value is calculated. KVO guarantees no order for the notifications, and although in a few cases it might optimize some repeated notifications away you don't have a lot of control over the quantity of notifications that get sent.
previous month april 2010 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    
Go to today