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.


