core-data crash on save.

  • Hi All,

    I've got a core-data relationship crash problem that I'd like to
    share, see if anyone out there has an idea of what I'm doing wrong.
    This is related to my recent question of knowing when an object is
    deleted (subject: core data - delete of object).

    The relationship in my graph is a to-many, from a Layer to an Effect
    (layers have many effects).  Obviously, both Layer and Effect are
    derived from NSManagedObject.

    In my program I have a check-box that switches a particular effect on/
    off, an opacity fade in this case.  Switching the effect off will
    delete the effect from the core-data graph in the following manner:
    A - remove the effect object from the list of effects on the layer
    (using core-data generated set accessor method)
    B - delete the effect instance (using the managed object context
    deleteObject: method)

    switching it on (via the checkbox) adds a new Effect instance back
    into the graph.  My requirements are, among other things:
      1 - when a layer effect is deleted, reflect the change immediately
    in the UI
      2 - don't orphan data objects in the DB

    requirement #1 here is the interesting one.  If I DO NOT perform Step
    A, then I fail requirement #1, because step (B), while working - is
    'lazy' in the sense that I'll only see the delete at save time.  if I
    DO NOT perform step B, then I end up orphaning the effect object (fail
    on #2), but I do fullfil requirement #1.  I'd like to have both -
    however, if I perform both Step A and Step B, I crash on save (with
    what appears to be a double delete - i.e. msg send to a dead object) :

    #0    0x92a8d688 in objc_msgSend
    #1    0x92c147c0 in NSKVOPendingNotificationCreate
    #2    0x92cc9b78 in -[NSObject(NSKeyValueObservingPrivate)
    _willChangeValuesForKeys:]
    #3    0x9269bafb in -[NSFaultHandler turnObject:intoFaultWithContext:]
    #4    0x92667596 in -[NSManagedObjectContext(_NSInternalChangeProcessing)
    _processRecentlyForgottenObjects:]
    #5    0x92666177 in -[NSManagedObjectContext(_NSInternalChangeProcessing)
    _processRecentChanges:]
    #6    0x92664e70 in -[NSManagedObjectContext executeFetchRequest:error:]
    #7    0x9556786c in -[_NSManagedProxy fetchObjectsWithFetchRequest:error:]
    #8    0x95409d2f in -[NSArrayController(NSManagedController)
    _performFetchWithRequest:merge:error:]
    #9    0x95566d97 in -[NSObjectController(NSManagedController)
    fetchWithRequest:merge:error:]
    #10    0x95567d9d in -[_NSManagedProxy _storesDidChange:]
    #11    0x92b941da in _nsnote_callback
    #12    0x96fa4aba in __CFXNotificationPost
    #13    0x96fa4d93 in _CFXNotificationPostNotification
    #14    0x92b91440 in -[NSNotificationCenter
    postNotificationName:object:userInfo:]
    #15    0x9265d9eb in -[NSPersistentStoreCoordinator(_NSInternalMethods)
    _postStoresChangedNotificationsForStores:changeKey:options:]
    #16    0x92652b40 in -[NSPersistentStoreCoordinator
    addPersistentStoreWithType:configuration:URL:options:error:]
    #17    0x9557e35b in -[NSPersistentDocument
    configurePersistentStoreCoordinatorForURL:ofType:modelConfiguration:storeOptions:error
    :]
    #18    0x00003cdb in -[AnnotateDocument
    configurePersistentStoreCoordinatorForURL:ofType:modelConfiguration:storeOptions:error
    :] at AnnotateDocument.m:143
    #19    0x9557f31d in -
    [NSPersistentDocument(NSPersistentDocumentDeprecated)
    configurePersistentStoreCoordinatorForURL:ofType:error:]
    #20    0x9557e8e7 in -[NSPersistentDocument
    writeToURL:ofType:forSaveOperation:originalContentsURL:error:]
    < ... snip ... >

    Are there core-data debugging macros, goodies, command line switches,
    env variables or some other magic I can use to help find the problem
    here?

    Thanks
    --
    John Clayton
    Skype: johncclayton
  • On Nov 18, 2008, at 00:27, John Clayton wrote:

    > In my program I have a check-box that switches a particular effect
    > on/off, an opacity fade in this case.  Switching the effect off will
    > delete the effect from the core-data graph in the following manner:
    > A - remove the effect object from the list of effects on the layer
    > (using core-data generated set accessor method)
    > B - delete the effect instance (using the managed object context
    > deleteObject: method)
    >
    > switching it on (via the checkbox) adds a new Effect instance back
    > into the graph.  My requirements are, among other things:
    > 1 - when a layer effect is deleted, reflect the change immediately
    > in the UI
    > 2 - don't orphan data objects in the DB
    >
    > requirement #1 here is the interesting one.  If I DO NOT perform
    > Step A, then I fail requirement #1, because step (B), while working
    > - is 'lazy' in the sense that I'll only see the delete at save
    > time.  if I DO NOT perform step B, then I end up orphaning the
    > effect object (fail on #2), but I do fullfil requirement #1. I'd
    > like to have both - however, if I perform both Step A and Step B, I
    > crash on save (with what appears to be a double delete - i.e. msg
    > send to a dead object) :

    You're not including some key information in this description. You
    have a to-many relationship Layer->Effect. Is there an inverse
    relationship Effect->Layer? If so, what is its delete rule?

    If you have an inverse relationship, and the Effect->Layer delete rule
    is Nullify, then it's not necessary to do part A -- it happens
    automatically, and #1 happens as result, assuming that the UI is
    properly watching for KVO notifications for the Layer's "effects"
    property.

    I suspect, though, that you actually have a memory management problem.
    If you do A first, then it could well be that the Effect object is
    immediately deallocated, unless you retain it before removing it from
    the relationship. (It was retained before the removal because it was
    in the NSSet representing the relationship, but possibly nowhere else.
    Managed object contexts don't routinely retain their objects unless
    you tell them to.) If you then do B next, you no longer have a valid
    object to pass to deleteObject.

    This might not be precisely what's happening, and of course it won't
    happen like this if you're using garbage collection, but memory
    management deserves some investigation, I think.
  • Hi there, comments embedded below...

    On 18/11/2008, at 7:06 PM, Quincey Morris wrote:

    > On Nov 18, 2008, at 00:27, John Clayton wrote:
    >
    >> In my program I have a check-box that switches a particular effect
    >> on/off, an opacity fade in this case.  Switching the effect off
    >> will delete the effect from the core-data graph in the following
    >> manner:
    >> A - remove the effect object from the list of effects on the layer
    >> (using core-data generated set accessor method)
    >> B - delete the effect instance (using the managed object context
    >> deleteObject: method)
    >>
    >> switching it on (via the checkbox) adds a new Effect instance back
    >> into the graph.  My requirements are, among other things:
    >> 1 - when a layer effect is deleted, reflect the change immediately
    >> in the UI
    >> 2 - don't orphan data objects in the DB
    >>
    >> requirement #1 here is the interesting one.  If I DO NOT perform
    >> Step A, then I fail requirement #1, because step (B), while working
    >> - is 'lazy' in the sense that I'll only see the delete at save
    >> time.  if I DO NOT perform step B, then I end up orphaning the
    >> effect object (fail on #2), but I do fullfil requirement #1. I'd
    >> like to have both - however, if I perform both Step A and Step B, I
    >> crash on save (with what appears to be a double delete - i.e. msg
    >> send to a dead object) :
    >
    > You're not including some key information in this description. You
    > have a to-many relationship Layer->Effect. Is there an inverse
    > relationship Effect->Layer? If so, what is its delete rule?
    >
    > If you have an inverse relationship, and the Effect->Layer delete
    > rule is Nullify, then it's not necessary to do part A -- it happens
    > automatically, and #1 happens as result, assuming that the UI is
    > properly watching for KVO notifications for the Layer's "effects"
    > property.
    >

    The delete rule is Nullify.  You are right of course, there should be
    no need to perform Step A if the rules are working OK.  But that's
    exactly what I'm saying - when I delete the object, nothing actually
    happens on the KVO notifications, zero.  That is, until I call
    processPendingChanges on the context.

    > I suspect, though, that you actually have a memory management
    > problem. If you do A first, then it could well be that the Effect
    > object is immediately deallocated, unless you retain it before
    > removing it from the relationship. (It was retained before the
    > removal because it was in the NSSet representing the relationship,
    > but possibly nowhere else. Managed object contexts don't routinely
    > retain their objects unless you tell them to.) If you then do B
    > next, you no longer have a valid object to pass to deleteObject.
    >
    > This might not be precisely what's happening, and of course it won't
    > happen like this if you're using garbage collection, but memory
    > management deserves some investigation, I think.
    >
    >

    So, with the extra two bits of knowledge, namely:

    - I have a Nullify relationship from Effect to the Layers 'effects'
    set/relationship
    - it works if I call processPendingChanges

    does it still 'reek' to you of something being broken?

    I'm a bit lost as to why I must call processPendingChanges to have my
    object deleted immediately.  It really does feel like I've screwed
    *something*, *somewhere*.  I thought the objects that I delete would
    really be deleted at the end of the event loop?
  • On Nov 18, 2008, at 3:53 PM, John Clayton wrote:

    <snip>

    > So, with the extra two bits of knowledge, namely:
    >
    > - I have a Nullify relationship from Effect to the Layers 'effects'
    > set/relationship
    > - it works if I call processPendingChanges
    >
    > does it still 'reek' to you of something being broken?
    >
    > I'm a bit lost as to why I must call processPendingChanges to have
    > my object deleted immediately.  It really does feel like I've
    > screwed *something*, *somewhere*.  I thought the objects that I
    > delete would really be deleted at the end of the event loop?

    You have to call processPendingChanges because you've disabled the
    context's undo support. As a consequence of that, the context will now
    only call processPendingChanges just before save (like it always did)
    but it will not call it at the end of the event loop since it doesn't
    need to generate undo operations.

    If you really want undo support disabled on the context you have to be
    responsible for calling processPendingChanges manually when you need
    to make other parts of your program aware of the changes.

    Ashley
  • On Nov 18, 2008, at 13:53, John Clayton wrote:

    > So, with the extra two bits of knowledge, namely:
    >
    > - I have a Nullify relationship from Effect to the Layers 'effects'
    > set/relationship
    > - it works if I call processPendingChanges
    >
    > does it still 'reek' to you of something being broken?
    >
    > I'm a bit lost as to why I must call processPendingChanges to have
    > my object deleted immediately.  It really does feel like I've
    > screwed *something*, *somewhere*.  I thought the objects that I
    > delete would really be deleted at the end of the event loop?

    If you'll excuse a short rant ...

    I think there's a semantic issue here, about what "deletion" means in
    Core Data. AFAIK, it's not referring to the lifetime of the object in
    your (in-memory) object graph, but the lifetime of the Core Data
    entity instance in your persistent store. If I am correct, deleting an
    object is "really" just an instruction to omit the object from the
    persistent store the next time it's saved.

    In those terms, it's not entirely surprising that the effect of
    "deleteObject" might be deferred from the moment you call it to some
    later time (processPendingChanges, or save, or whatever). The actual
    object doesn't get deleted by "deleteObject", only its persistence in
    the store. The object gets *deallocated* when there are no references
    to it (regardless of whether it's deleted in the store or not).

    I would have expected the delete rule to be applied at "deleteObject"
    time, but it seems not completely illogical to do it later. (Part of
    the reason for processPendingChanges is, I believe, to avoid tracking
    a lot of redundant individual changes in the undo manager, and that's
    a Good Thing.)

    It might be worthwhile filing a bug on this, saying that the delete
    rule ought to be applied at a predictable time, or that the
    documentation should be updated to make it clear *when* it's done.

    In practical terms, I'm suggesting you continue with your A/B
    methodology:

    [effect retain];
    effect.layer = nil; // let Core Data do the work for the inverse
    [moc deleteObject: effect];
    [effect release];

    I don't think this reeks of something broken, it just reflects a piece
    of complexity in Core Data's semantics: there really are 2 things you
    need to do -- one piece of object graph management and one piece of
    persistent store management.

    If it still crashes, then I would investigate the issue as a memory
    management problem first.
  • For all that answered me - a huge thanks.  I've solved my particular
    flavour of this problem now.

    Partly, its a problem of the documentation - and partly my particular
    configuration.  Here, for future reference at the 'facts' I collected
    along my learning experience - which led me finally to a solution.

    My particular gotchas were:
    - performing a fetch during the
    initWithEntity:insertIntoManagedObjectContext: (caused a crash after
    deleteObject:)
    - I have set the undo manager to nil in the context, because I want to
    control it, this has the side effect that processPendingChanges does
    NOT get called at the end of the event loop (even if you explicitly
    set it to do so in the context).

    1. a standard 10.5 core-data project includes undo support - which
    implies (not obviously) that processPendingChanges gets called at the
    end of the event loop automatically.

    2. processPendingChanges is responsible for deleting objects that you
    nuked with [context deleteObject:] - and this method will only be
    called if you actually have an undo manager in your core data
    context.  (I dont - I explicitly disabled it because I don't want such
    a fine grained, uncontrollable core-data undo architecture - thats
    another story for another time though)

    3. during the following methods, one should NEVER modify the state of
    the data-model - from the docs:

    Methods to Override Considerations

        The following methods are intended to be fine grained and not
    perform
        large scale operations. You must not fetch or save in these
    methods. In
        particular, they should not have side effects on the managed object
        context:

            �    initWithEntity:insertIntoManagedObjectContext:
            �    didTurnIntoFault
            �    dealloc

    now, I *was* indirectly performing a fetch within the my
    awakeFromInsert method - and that's bad, because awakeFromInsert is
    called from within initWithEntity:insertIntoManagedContext:  ... and
    processPendingChanges can get/fetch as well, so the golden rule there
    is easy: don't fetch, dont cause processPendingChanges: to be called
    during the above three methods.

    4. if doing some entirely non-user-initiated mods to the core-data
    layer (i.e. from a sep thread), then the objects must be (a) added
    into the core data context on the main thread, (b) you must call
    processPendingChanges on the context to apply the changes.

    Point (4) didn't apply to me as I'm not doing any off-UI-thread work,
    but its worth adding to the list.

    ---------------------

    So, lots of 'issuettes' with my code there.  Now I understand why it
    crashed, why my initial solutions failed, why refactoring it has
    worked and most importantly, how I can continue / move forward.

    So all in all, success - my hat goes off to the people that took the
    time to answer me - thank you.

    --
    John Clayton
    Skype: johncclayton

    On 19/11/2008, at 12:43 AM, Quincey Morris wrote:

    > On Nov 18, 2008, at 13:53, John Clayton wrote:
    >
    >> So, with the extra two bits of knowledge, namely:
    >>
    >> - I have a Nullify relationship from Effect to the Layers 'effects'
    >> set/relationship
    >> - it works if I call processPendingChanges
    >>
    >> does it still 'reek' to you of something being broken?
    >>
    >> I'm a bit lost as to why I must call processPendingChanges to have
    >> my object deleted immediately.  It really does feel like I've
    >> screwed *something*, *somewhere*.  I thought the objects that I
    >> delete would really be deleted at the end of the event loop?
    >
    > If you'll excuse a short rant ...
    >
    > I think there's a semantic issue here, about what "deletion" means
    > in Core Data. AFAIK, it's not referring to the lifetime of the
    > object in your (in-memory) object graph, but the lifetime of the
    > Core Data entity instance in your persistent store. If I am correct,
    > deleting an object is "really" just an instruction to omit the
    > object from the persistent store the next time it's saved.
    >
    > In those terms, it's not entirely surprising that the effect of
    > "deleteObject" might be deferred from the moment you call it to some
    > later time (processPendingChanges, or save, or whatever). The actual
    > object doesn't get deleted by "deleteObject", only its persistence
    > in the store. The object gets *deallocated* when there are no
    > references to it (regardless of whether it's deleted in the store or
    > not).
    >
    > I would have expected the delete rule to be applied at
    > "deleteObject" time, but it seems not completely illogical to do it
    > later. (Part of the reason for processPendingChanges is, I believe,
    > to avoid tracking a lot of redundant individual changes in the undo
    > manager, and that's a Good Thing.)
    >
    > It might be worthwhile filing a bug on this, saying that the delete
    > rule ought to be applied at a predictable time, or that the
    > documentation should be updated to make it clear *when* it's done.
    >
    > In practical terms, I'm suggesting you continue with your A/B
    > methodology:
    >
    > [effect retain];
    > effect.layer = nil; // let Core Data do the work for the inverse
    > [moc deleteObject: effect];
    > [effect release];
    >
    > I don't think this reeks of something broken, it just reflects a
    > piece of complexity in Core Data's semantics: there really are 2
    > things you need to do -- one piece of object graph management and
    > one piece of persistent store management.
    >
    > If it still crashes, then I would investigate the issue as a memory
    > management problem first.
previous month november 2008 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
MindNode
MindNode offered a free license !