Console: *** -observeValueForKeyPath:... only defined for abstract class

  • I've added a custom control which displays data from a "detail" (as
    in "master/detail") NSArrayController which it observes.  That array
    controller is in Entity mode, and this is  a Core Data project.

    Sometimes, while manipulating the control or the model data which is
    bound into this array controller, I get the following message in the
    console:

    *** -observeValueForKeyPath:ofObject:change:context: only defined for
    abstract class.
        Define -[NSKeyValueObservance
    observeValueForKeyPath:ofObject:change:context:]!

    The method referred to there is is the workhorse which we use to
    receive KVO notifications.  It is defined in the "NSKeyValueObserving
    Protocol Reference"
    API.  The protocol overview says that it is "provided" for "all
    objects", although it is informal.

    So, what might they mean "only defined for abstract class"??  The API
    does not say anything about "abstract classes".

    Thanks,

    Jerry Krinock

    More info.  My project is a simple demo project with no non-Cocoa
    frameworks.  Searching the project for the offensive method, I've
    confirmed that I've implemented it in two places...

    The first implementation is in my custom control.  This custom
    control is a subclass of NSView, and it is observing the
    'arrangedObjects' in the "detail" array controller, and also two keys
    of the objects managed in the array controller, which it displays.
    These objects are subclasses of NSManagedObject, and were generated
    by mogenerator.  I've read that 'arrangedObjects' may not be a good
    key to observe since it is not KVC, but it seems to work, and is the
    only way I can figure out to get notified of changes caused by a
    changing filter predicate.  (I suppose I could write my own KVC array
    controller with the filtering and sorting features that I need
    instead of using NSArrayController, but I'd like to avoid that if
    possible.)

    The second implementation is in my _AppDelegate, a subclass of
    NSObject.  It is observing the 'selectedObjects' of a "master" array
    controller, so that it can setFilterPredicate: of the "detail" array
    controller to only show objects "selected" in the master.

    I keep track of the managed objects that the custom view observing by
    means of an array ivar in my custom view.  Whenever the array
    changes, I remove the custom view as an observer of all the managed
    objects, for the two keyPaths that I am observing, then repopulate
    this array with the new managed objects as I add them to the view.
    I've logged my invocations of addObserver:forKeyPath:options:context
    and invocations of removeObserver:forKeyPath and verified that this
    all seems to be working; i.e. all added observers are always removed
    when the array changes.
  • On 24.09.2007, at 01:55, Jerry Krinock wrote:

    > Sometimes, while manipulating the control or the model data which
    > is bound into this array controller, I get the following message in
    > the console:
    >
    > *** -observeValueForKeyPath:ofObject:change:context: only defined
    > for abstract class.
    > Define -[NSKeyValueObservance
    > observeValueForKeyPath:ofObject:change:context:]!
    >
    > The method referred to there is is the workhorse which we use to
    > receive KVO notifications.  It is defined in the
    > "NSKeyValueObserving Protocol Reference"
    > API.  The protocol overview says that it is "provided" for "all
    > objects", although it is informal.
    >
    > So, what might they mean "only defined for abstract class"??  The
    > API does not say anything about "abstract classes".

    Without going into detail for your specific problem, I'd try to break
    when that message is output and then inspect which object outputs the
    message. The API will not tell you about "abstract classes" because
    they are not an API concept but an programming pattern. Quoting the
    book "Design Patterns", "An abstract class is one whose main purpose
    is to define the interface for it's subclasses". Note it is a pretty
    common C++ idiom, but rather rare in Objective-C, where protocols are
    used for interfaces.

      If you are lucky, a breakpoint on [NSException raise] or NSLog will
    work.

    If the error does not occur 100% reproducible, it might be bad memory
    management - an object deallocated and in the same memory location
    repalced with something completely different, whose method is invoked.
  • On 2007 Sep, 24, at 2:04, Thomas Engelmeier wrote:

    > Without going into detail for your specific problem, I'd try to
    > break when that message is output and then inspect which object
    > outputs the message....If you are lucky, a breakpoint on
    > [NSException raise] or NSLog will work.
    >
    > If the error does not occur 100% reproducible, it might be bad
    > memory management - an object deallocated and in the same memory
    > location repalced with something completely different, whose method
    > is invoked.

    Thank you, Thomas.  Further testing confirmed that it is not 100%
    reproducible.  Besides NSKeyValueObservance, sometimes the errant
    message gets sent to an NSIdEnumerator and sometimes
    objc_message_send crashes.  So, yes, it looks like a case of invalid
    message receiver.

    To troubleshoot this, I defined observeValueForKeyPath:::: in a
    category on NSObject, so that way any time that message was sent to
    an object which didn't handle it, I'd get a log.

    I've pasted in a typical log below.  Indeed, the "changing" 'object'
    is one of my NSManagedObjects, and the 'change' is the change I had
    just made.  But there are two things I find surprising:

    (a) The observationInfo of the observed object lists three
    observers.  The last two are ones that I added.  They are still
    valid.  But the first one is a mysterious
    NSKeyValueObservationForwarder.  Does that give anyone a clue?

    (b) However, the object receiving this observeValueForKeyPath::::
    message is not even one of those three observers.  How it receiving
    this message?  (Possible unhelpful answer: because it was forwarded
    by that "...Forwarder" thingey.)

    I've searched my code several times for addObserver::: and bind::::
    messages, but the only invocations I can find on objects of this
    class ('Entry') are the two which are causing the two expected
    observers I see in the observationInfo.

    Jerry

    *****************************************************
    Typical log "catch" (abridged to improve readability)
    *****************************************************

    Internal Error!!! Stray observation message received!!
    self = NSIdEnumerator 3261b0 <NSIdEnumerator: 0x3261b0>
    keyPath = swimmer
    object = Entry 330130 Entry at position 4 in 800 Free for Caroline
    change = {
        kind = 1;
        new = <Swimmer: 0x32bf30> (
              entity: Swimmer;
              id: 0x32bf60 <x-coredata:///Swimmer/tblahblahUUID> ;
              data: {
                        entries = (0x325c00 <x-coredata:///Entry/
    tblahblahUUID>);
                        name = fishc;
              }
              );
        old = <null>;
    }
    context = 3261b0

    infoString = <NSKeyValueObservance:
              Observer: 0x14a06770,
              Key path: swimmer,
              Include old: YES,
              Include new: YES,
              Context: 0x14a06770,
              Property: 0x33b3a0>,
    observer is: <NSKeyValueObservationForwarder>
    <NSKeyValueObservationForwarder: 0x14a06770>

    infoString = <NSKeyValueObservance:
              Observer: 0x394740,
              Key path: swimmer.name,
              Include old: NO,
              Include new: NO,
              Context: 0x892,
              Property: 0x34c980>,
    observer is: <TextFieldListView> <TextFieldListView: 0x394740>

    infoString = <NSKeyValueObservance:
              Observer: 0x394740,
              Key path: position,
              Include old: NO,
              Include new: NO,
              Context: 0x892,
              Property: 0x323c90>
    observer is: <TextFieldListView> <TextFieldListView: 0x394740>

    ***********************
    Code producing that log
    ***********************

    @interface NSObject (JerryDebug)

    - (void)observeValueForKeyPath:(NSString *)keyPath
                          ofObject:(id)object
                            change:(NSDictionary *)change
                            context:(void *)context ;
    @end

    @implementation NSObject (JerryDebug)
    - (void)observeValueForKeyPath:(NSString *)keyPath
                          ofObject:(id)object
                            change:(NSDictionary *)change
                            context:(void *)context {
        NSLog(@"Internal Error!!! Stray observation message received!!") ;
        NSLog(@"self = %@ %x %@", [self class], self, self) ;
        NSLog(@"keyPath = %@", keyPath) ;
        NSLog(@"object = %@ %x %@", [object class], object, object) ;
        NSLog(@"change = %@", change) ;
        NSLog(@"context = %x", context) ;
        NSArray* observationInfos = [object observationInfo] ;
        NSDictionary* info ;
        NSEnumerator* e = [observationInfos objectEnumerator] ;
        while((info = [e nextObject])) {
            NSString* infoString = [info description] ;
            NSLog(@"infoString = %@", infoString) ;
            NSScanner* scanner = [[NSScanner alloc]
    initWithString:infoString] ;
            [scanner scanUpToString:@": 0x" intoString:NULL] ;
            [scanner scanString:@": 0x" intoString:NULL] ;
            NSString* hexAddressString ;
            [scanner scanUpToString:@"," intoString:&hexAddressString] ;
            [scanner release] ;
            const char* hexAddressC = [hexAddressString UTF8String] ;
            long hexAddress ;
            sscanf(hexAddressC, "%x", &hexAddress) ;
            id observer = (id)hexAddress ;
            NSLog(@"observer is: <%@> %@", [observer class], observer) ;
        }
        NSBeep() ;
    }

    @end
  • On 26.09.2007, at 02:34, Jerry Krinock wrote:

    > On 2007 Sep, 24, at 2:04, Thomas Engelmeier wrote:
    >
    >> Without going into detail for your specific problem, I'd try to
    >> break when that message is output and then inspect which object
    >> outputs the message....If you are lucky, a breakpoint on
    >> [NSException raise] or NSLog will work.
    >>
    >> If the error does not occur 100% reproducible, it might be bad
    >> memory management - an object deallocated and in the same memory
    >> location repalced with something completely different, whose
    >> method is invoked.
    >
    > Thank you, Thomas.  Further testing confirmed that it is not 100%
    > reproducible.  Besides NSKeyValueObservance, sometimes the errant
    > message gets sent to an NSIdEnumerator and sometimes
    > objc_message_send crashes.  So, yes, it looks like a case of
    > invalid message receiver.
    >
    > To troubleshoot this, I defined observeValueForKeyPath:::: in a
    > category on NSObject, so that way any time that message was sent to
    > an object which didn't handle it, I'd get a log.

    I have simply no idea about CoreData intrinsics, but I'm sure the
    normal debug features like setting NSZombieEnabled and CFZombieLevel
    will at least help tracing that down... <http://www.cocoadev.com/
    index.pl?NSZombieEnabled
    >

    HTH,
    Tom_E
previous month september 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