KVO problems popping up in Leopard

  • Hi, all,

    The app I've been working on is suddenly very broken under Leopard.  The
    most severe problem right now occurs when I edit a column cell in an
    NSOutlineView: on committing the changes, I get an EXC_BAD_ACCESS,
    apparently related to a notification to the object whose value is being
    edited.

    The NSTreeController is in class mode, binding to "proxy" objects that I set
    up between the controller and the underlying Core Data objects.  The error
    occurs when I try to commit a change in a column bound to a property on the
    proxy object; specifically, according to the stack trace, when attempting to
    invoke "willChangeValueForKey:" for the modified property.  I've made
    several changes to the code to try to clean it up and/or instrument it;
    here's a current stack trace (annotated):

    #0 0x92f646e8 in objc_msgSend
    #1 0x94fac880 in NSKVOPendingNotificationCreate
    #2 0x94f2958d in -[NSObject(NSKeyValueObserverNotification)
    willChangeValueForKey:]
    #3 0x000315e4 in -[ListViewItemProxy willChangeValueForKey:] at
    ListViewItemProxy.m:487
      (this is an instrumentation method I added to figure out which property
    is involved...)
    #4 0x94fac2ed in _NSSetObjectValueAndNotify
    #5 0x94f4e52e in -[NSObject(NSKeyValueCoding) setValue:forKey:]
    #6 0x94fd23f1 in -[NSObject(NSKeyValueCoding) setValue:forKeyPath:]
    #7 0x93d98b1c in -[NSTreeController
    _setMultipleValue:forKeyPath:atIndexPath:]
    #8 0x93a2da5b in -[NSBinder
    _setValue:forKeyPath:ofObject:mode:validateImmediately:raisesForNotApplicableKeys:error:]
    #9 0x93b407f9 in -[NSBinder setValue:forBinding:atIndexPath:error:]
    #10 0x93a2d377 in -[_NSValueBinderPlugin
    applyObjectValue:forBinding:operation:needToRunAlert:error:]
    #11 0x93a2d1aa in -[NSValueBinder
    _applyObjectValue:forBinding:canRecoverFromErrors:handleErrors:typeOfAlert:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:]
    #12 0x93a2cea9 in -[NSValueBinder
    applyDisplayedValueHandleErrors:typeOfAlert:canRecoverFromErrors:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:]
    #13 0x93a2fbfd in -[NSValueBinder
    _applyDisplayedValueIfHasUncommittedChangesWithHandleErrors:typeOfAlert:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:]
    #14 0x93a2c78a in -[NSValueBinder
    validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled:]
    #15 0x93a2c547 in -[_NSBindingAdaptor
    _validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled:bindingAdaptor:]
    #16 0x93a2c436 in -[_NSBindingAdaptor
    validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled:]
    #17 0x93d1279b in -[NSTableView textShouldEndEditing:]

    I'm really stuck here; this is code that worked fine under Tiger.  Any
    advice on how to debug this is more than welcome.
    TIA,
    Doug K;
  • Doug,

    Most likely your proxy is trying to notify an observer which has been
    deallocated, so the deallocated object has not removed it's
    observation for whatever reason.

    You might get more information by enabling zombies.

    A brute-force approach to locating the culprit is to override the kvo
    methods -addObserver:... and -removeObserver:... in your proxy class
    and log the observer's class name and address.  Try to reduce the
    number of log messages by restricting attention to a specific key and
    a specific receiver; use a global variable to mark a single proxy
    object, setting its value explicitly in gdb or in your code as the
    first receiver of -addObserver:...

    You may be able to see the address of the deallocated object as the
    first argument to objc_msgSend.  If not, use 'po' to check that the
    objects with registered observations are still valid.  If you find a
    deallocated object of a class you recognize, ensure that it is
    properly removing observations.  If the deallocated object is an
    NSKeyValueForwarder (or whatever) then likely an observation is being
    established on a key path and some object in the path is not
    generating key/value notification on property changes.

    Hope this helps,

    dave

    On 14-Nov-07, at 8:01 PM, Doug Knowles wrote:

    > Hi, all,
    >
    > The app I've been working on is suddenly very broken under Leopard.
    > The
    > most severe problem right now occurs when I edit a column cell in an
    > NSOutlineView: on committing the changes, I get an EXC_BAD_ACCESS,
    > apparently related to a notification to the object whose value is
    > being
    > edited.
    >
    > The NSTreeController is in class mode, binding to "proxy" objects
    > that I set
    > up between the controller and the underlying Core Data objects.  The
    > error
    > occurs when I try to commit a change in a column bound to a property
    > on the
    > proxy object; specifically, according to the stack trace, when
    > attempting to
    > invoke "willChangeValueForKey:" for the modified property.  I've made
    > several changes to the code to try to clean it up and/or instrument
    > it;
    > here's a current stack trace (annotated):
    >
    > #0 0x92f646e8 in objc_msgSend
    > #1 0x94fac880 in NSKVOPendingNotificationCreate
    > #2 0x94f2958d in -[NSObject(NSKeyValueObserverNotification)
    > willChangeValueForKey:]
    > #3 0x000315e4 in -[ListViewItemProxy willChangeValueForKey:] at
    > ListViewItemProxy.m:487
    > (this is an instrumentation method I added to figure out which
    > property
    > is involved...)
    > #4 0x94fac2ed in _NSSetObjectValueAndNotify
    > #5 0x94f4e52e in -[NSObject(NSKeyValueCoding) setValue:forKey:]
    > #6 0x94fd23f1 in -[NSObject(NSKeyValueCoding) setValue:forKeyPath:]
    > #7 0x93d98b1c in -[NSTreeController
    > _setMultipleValue:forKeyPath:atIndexPath:]
    > #8 0x93a2da5b in -[NSBinder
    > _setValue:forKeyPath:ofObject:mode:validateImmediately:raisesForNotApplicableKeys:error
    > :]
    > #9 0x93b407f9 in -[NSBinder setValue:forBinding:atIndexPath:error:]
    > #10 0x93a2d377 in -[_NSValueBinderPlugin
    > applyObjectValue:forBinding:operation:needToRunAlert:error:]
    > #11 0x93a2d1aa in -[NSValueBinder
    > _applyObjectValue:forBinding:canRecoverFromErrors:handleErrors:typeOfAlert:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert
    > :]
    > #12 0x93a2cea9 in -[NSValueBinder
    > applyDisplayedValueHandleErrors:typeOfAlert:canRecoverFromErrors:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert
    > :]
    > #13 0x93a2fbfd in -[NSValueBinder
    > _applyDisplayedValueIfHasUncommittedChangesWithHandleErrors:typeOfAlert:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert
    > :]
    > #14 0x93a2c78a in -[NSValueBinder
    > validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled
    > :]
    > #15 0x93a2c547 in -[_NSBindingAdaptor
    > _validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled:bindingAdaptor
    > :]
    > #16 0x93a2c436 in -[_NSBindingAdaptor
    > validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled
    > :]
    > #17 0x93d1279b in -[NSTableView textShouldEndEditing:]
    >
    > I'm really stuck here; this is code that worked fine under Tiger.  Any
    > advice on how to debug this is more than welcome.
    > TIA,
    > Doug K;
  • Hi, Dave,
    Thanks for your help; I've been poking at this some more, following your
    advice and a couple of other threads for the last couple of days.

    As luck would have it, I was already tracking the registered observers of my
    proxy objects, so I was able to determine that there is one observer of the
    property in question at the time of the bad access exception, and it is
    still valid, but the associated context looks suspicious - I get a debugger
    crash when I try to examine it after the exception:

    2007-11-16 08:29:08.388 SLNavigator[2102:813] @@@ LVProxyLinkedItem
    0xcda1ad0 adding observer (<NSKeyValueObservance 0xcda8300: Observer:
    0xcd65450, Key path: representedObject.lvName, Options: <New: NO, Old: NO,
    Prior: NO> Context: 0xa05a028c, Property: 0xcd6e6d0>) for key path: lvName

    ...

    2007-11-16 08:29:13.540 SLNavigator[2102:813] DEBUG: -[ListViewItemProxy
    willChangeValueForKey:] for lvName
    Program received signal:  "EXC_BAD_ACCESS".
    (gdb) po 0xcda8300
    <NSKeyValueObservance 0xcda8300: Observer: 0xcd65450, Key path:
    representedObject.lvName, Options: <New: NO, Old: NO, Prior: NO> Context:
    0xa05a028c, Property: 0xcd6e6d0>
    (gdb) po 0xcd65450
    <NSOutlineViewBinder: 0xcd65450>{object: <ListOutlineView: 0xcd426c0>,
    bindings: content=arrangedObjects, selectionIndexPaths=selectionIndexPaths,
    sortDescriptors=sortDescriptors}
    (gdb) po 0xa05a028c            // <-- [Context field of the
    NSKeyValueObservance object above]

    Program received signal EXC_BAD_ACCESS, Could not access memory.
    Reason: KERN_PROTECTION_FAILURE at address: 0x00000020
    0x92f646e8 in objc_msgSend ()
    The program being debugged was signaled while in a function called from GDB.
    GDB has restored the context to what it was before the call.
    To change this behavior use "set unwindonsignal off"
    Evaluation of the expression containing the function (_NSPrintForDebugger)
    will be abandoned.

    On a subsequent run, I examined the same "context" value at the time the
    observer was registered:

    *(gdb) **po 0xcd6efa0*

    <NSKeyValueNestedProperty: Container class: NSTreeControllerTreeNode,
    Relationship property: <NSKeyValueUnnestedProperty: Container class:
    NSTreeControllerTreeNode, Key: representedObject, isa for autonotifying:
    NSKVONotifying_NSTreeControllerTreeNode, Key paths of directly and
    indirectly affecting properties: none>, Key path from related object:
    lvName>

    ...but I don't see why I'm getting the crash in objc_msgSend if the problem
    is a member of the object being messaged.

    The other things I'm looking at are liberties I take with the KVO protocol:

    - My proxy objects implement valueForUndefinedKey: and
    setValue:forUndefinedKey: to present properties derived from the model
    objects they represent;
    - they also invoke will... and didChangeValueForKey: on these "undefined"
    keys to trigger refresh of the derived values;
    - the property in question, "lvName", is one of these derived values; since
    I encountered this problem, I implemented accessors for this property so
    that it wouldn't pass through the undefined key methods, but it is still a
    "non-standard" KVO property.

    Thanks for the advice so far...

    Doug K;

    On Nov 15, 2007 2:19 AM, David Spooner <dave...> wrote:

    > Doug,
    >
    > Most likely your proxy is trying to notify an observer which has been
    > deallocated, so the deallocated object has not removed it's
    > observation for whatever reason.
    >
    > You might get more information by enabling zombies.
    >
    > A brute-force approach to locating the culprit is to override the kvo
    > methods -addObserver:... and -removeObserver:... in your proxy class
    > and log the observer's class name and address.  Try to reduce the
    > number of log messages by restricting attention to a specific key and
    > a specific receiver; use a global variable to mark a single proxy
    > object, setting its value explicitly in gdb or in your code as the
    > first receiver of -addObserver:...
    >
    > You may be able to see the address of the deallocated object as the
    > first argument to objc_msgSend.  If not, use 'po' to check that the
    > objects with registered observations are still valid.  If you find a
    > deallocated object of a class you recognize, ensure that it is
    > properly removing observations.  If the deallocated object is an
    > NSKeyValueForwarder (or whatever) then likely an observation is being
    > established on a key path and some object in the path is not
    > generating key/value notification on property changes.
    >
    > Hope this helps,
    >
    > dave
    >
    >
    > On 14-Nov-07, at 8:01 PM, Doug Knowles wrote:
    >
    >> Hi, all,
    >>
    >> The app I've been working on is suddenly very broken under Leopard.
    >> The
    >> most severe problem right now occurs when I edit a column cell in an
    >> NSOutlineView: on committing the changes, I get an EXC_BAD_ACCESS,
    >> apparently related to a notification to the object whose value is
    >> being
    >> edited.
    >>
    >> The NSTreeController is in class mode, binding to "proxy" objects
    >> that I set
    >> up between the controller and the underlying Core Data objects.  The
    >> error
    >> occurs when I try to commit a change in a column bound to a property
    >> on the
    >> proxy object; specifically, according to the stack trace, when
    >> attempting to
    >> invoke "willChangeValueForKey:" for the modified property.  I've made
    >> several changes to the code to try to clean it up and/or instrument
    >> it;
    >> here's a current stack trace (annotated):
    >>
    >> #0 0x92f646e8 in objc_msgSend
    >> #1 0x94fac880 in NSKVOPendingNotificationCreate
    >> #2 0x94f2958d in -[NSObject(NSKeyValueObserverNotification)
    >> willChangeValueForKey:]
    >> #3 0x000315e4 in -[ListViewItemProxy willChangeValueForKey:] at
    >> ListViewItemProxy.m:487
    >> (this is an instrumentation method I added to figure out which
    >> property
    >> is involved...)
    >> #4 0x94fac2ed in _NSSetObjectValueAndNotify
    >> #5 0x94f4e52e in -[NSObject(NSKeyValueCoding) setValue:forKey:]
    >> #6 0x94fd23f1 in -[NSObject(NSKeyValueCoding) setValue:forKeyPath:]
    >> #7 0x93d98b1c in -[NSTreeController
    >> _setMultipleValue:forKeyPath:atIndexPath:]
    >> #8 0x93a2da5b in -[NSBinder
    >>
    > _setValue:forKeyPath:ofObject:mode:validateImmediately:raisesForNotApplicableKeys:error
    >> :]
    >> #9 0x93b407f9 in -[NSBinder setValue:forBinding:atIndexPath:error:]
    >> #10 0x93a2d377 in -[_NSValueBinderPlugin
    >> applyObjectValue:forBinding:operation:needToRunAlert:error:]
    >> #11 0x93a2d1aa in -[NSValueBinder
    >>
    > _applyObjectValue:forBinding:canRecoverFromErrors:handleErrors:typeOfAlert:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert
    >> :]
    >> #12 0x93a2cea9 in -[NSValueBinder
    >>
    > applyDisplayedValueHandleErrors:typeOfAlert:canRecoverFromErrors:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert
    >> :]
    >> #13 0x93a2fbfd in -[NSValueBinder
    >>
    > _applyDisplayedValueIfHasUncommittedChangesWithHandleErrors:typeOfAlert:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert
    >> :]
    >> #14 0x93a2c78a in -[NSValueBinder
    >> validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled
    >> :]
    >> #15 0x93a2c547 in -[_NSBindingAdaptor
    >>
    > _validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled:bindingAdaptor
    >> :]
    >> #16 0x93a2c436 in -[_NSBindingAdaptor
    >> validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled
    >> :]
    >> #17 0x93d1279b in -[NSTableView textShouldEndEditing:]
    >>
    >> I'm really stuck here; this is code that worked fine under Tiger.  Any
    >> advice on how to debug this is more than welcome.
    >> TIA,
    >> Doug K;
    >
  • On 18-Nov-07, at 5:37 PM, Doug Knowles wrote:

    > Hi, Dave,
    >
    > Thanks for your help; I've been poking at this some more, following
    > your advice and a couple of other threads for the last couple of days.
    >
    > As luck would have it, I was already tracking the registered
    > observers of my proxy objects, so I was able to determine that there
    > is one observer of the property in question at the time of the bad
    > access exception, and it is still valid, but the associated context
    > looks suspicious - I get a debugger crash when I try to examine it
    > after the exception:
    >
    > 2007-11-16 08:29:08.388 SLNavigator[2102:813] @@@ LVProxyLinkedItem
    > 0xcda1ad0 adding observer (<NSKeyValueObservance 0xcda8300:
    > Observer: 0xcd65450, Key path: representedObject.lvName, Options:
    > <New: NO, Old: NO, Prior: NO> Context: 0xa05a028c, Property:
    > 0xcd6e6d0>) for key path: lvName
    >
    > ...
    >
    > 2007-11-16 08:29:13.540 SLNavigator[2102:813] DEBUG: -
    > [ListViewItemProxy willChangeValueForKey:] for lvName
    > Program received signal:  "EXC_BAD_ACCESS".
    > (gdb) po 0xcda8300
    > <NSKeyValueObservance 0xcda8300: Observer: 0xcd65450, Key path:
    > representedObject.lvName, Options: <New: NO, Old: NO, Prior: NO>
    > Context: 0xa05a028c, Property: 0xcd6e6d0>
    > (gdb) po 0xcd65450
    > <NSOutlineViewBinder: 0xcd65450>{object: <ListOutlineView:
    > 0xcd426c0>, bindings: content=arrangedObjects,
    > selectionIndexPaths=selectionIndexPaths,
    > sortDescriptors=sortDescriptors}
    > (gdb) po 0xa05a028c            // <-- [Context field of the
    > NSKeyValueObservance object above]
    >
    > Program received signal EXC_BAD_ACCESS, Could not access memory.
    > Reason: KERN_PROTECTION_FAILURE at address: 0x00000020
    > 0x92f646e8 in objc_msgSend ()
    > The program being debugged was signaled while in a function called
    > from GDB.
    > GDB has restored the context to what it was before the call.
    > To change this behavior use "set unwindonsignal off"
    > Evaluation of the expression containing the function
    > (_NSPrintForDebugger) will be abandoned.
    >
    > On a subsequent run, I examined the same "context" value at the time
    > the observer was registered:
    >
    > (gdb) po 0xcd6efa0
    > <NSKeyValueNestedProperty: Container class:
    > NSTreeControllerTreeNode, Relationship property:
    > <NSKeyValueUnnestedProperty: Container class:
    > NSTreeControllerTreeNode, Key: representedObject, isa for
    > autonotifying: NSKVONotifying_NSTreeControllerTreeNode, Key paths of
    > directly and indirectly affecting properties: none>, Key path from
    > related object: lvName>
    >
    > ...but I don't see why I'm getting the crash in objc_msgSend if the
    > problem is a member of the object being messaged.

    I had (wrongly) assumed the crash occurred in sending -
    observeValueForKeyPath:... to the observer, but looking at your
    original stack trace the crash occurs via -willChangeValueForKey:.  I
    now suspect the problem is in obtaining the old value for the change
    dictionary.

    > The other things I'm looking at are liberties I take with the KVO
    > protocol:
    >
    > - My proxy objects implement valueForUndefinedKey: and
    > setValue:forUndefinedKey: to present properties derived from the
    > model objects they represent;
    > - they also invoke will... and didChangeValueForKey: on these
    > "undefined" keys to trigger refresh of the derived values;
    > - the property in question, "lvName", is one of these derived
    > values; since I encountered this problem, I implemented accessors
    > for this property so that it wouldn't pass through the undefined key
    > methods, but it is still a "non-standard" KVO property.

    Do your proxy objects shadow the attributes of the model objects (say
    in a dictionary), or does -valueForUndefinedKey: simply return [model
    valueForKey:]?  If the latter, do your proxy objects observe the
    attributes of the model and invoke will/did change methods from their
    observation method or are will/did invoked directly from -
    setValue:forUndefinedKey:?

    It might be worth considering the shadowing approach.  Here your proxy
    would maintain a dictionary of the relevant model properties; it would
    observe those model properties and update its dictionary accordingly;
    its -valueForUndefinedKey: would return a value from the dictionary;
    and its -setValue:forUndefinedKey: would simply invoke the model's
    setValue:forKey:.  This approach avoids the need to explicitly invoke
    the will/did change methods...

    > Thanks for the advice so far...
    >
    > Doug K;

    Cheers,
    dave
  • On Nov 19, 2007 2:10 AM, David Spooner <dave...> wrote:

    > Do your proxy objects shadow the attributes of the model objects (say in a
    > dictionary), or does -valueForUndefinedKey: simply return [model
    > valueForKey:]?  If the latter, do your proxy objects observe the attributes
    > of the model and invoke will/did change methods from their observation
    > method or are will/did invoked directly from -setValue:forUndefinedKey:?
    >

    I've been invoking the will/did change methods from the observe... method
    for the model object keys.

    >
    > It might be worth considering the shadowing approach.  Here your proxy
    > would maintain a dictionary of the relevant model properties; it would
    > observe those model properties and update its dictionary accordingly; its
    > -valueForUndefinedKey: would return a value from the dictionary; and its
    > -setValue:forUndefinedKey: would simply invoke the model's setValue:forKey:.
    > This approach avoids the need to explicitly invoke the will/did change
    > methods...
    >

    For the property in question, I implemented getter/setter methods in the
    proxy since I started encountering this problem; these methods provided no
    backing store for the property, so I've been returning NO from +
    accessInstanceVariablesDirectly.  Your suggestion to maintain a dictionary
    is a more general solution for the other (dynamically generated properties)
    my proxy; I'll try that today.

    Thanks again for your time and assistance!
previous month november 2007 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