Mmmm, the plot thickens...

I think this has to do with two MOCs being out of synchronisation, despite my best efforts. The specific situation is that I have two threads - the main thread and one other. I've created a new test harness to try to isolate the problem, which does the following:

- Setup on Thread 1 (the main thread):
- Create an instance of A, and a connected instance of B (of subentity 1). Save.
- On Thread 2:
- Swap A's object B for a new instance (of subentity 2), save, swap A's object B for a new instance (of subentity 3), save
- On Thread 1 (the main thread):
- Swap A's object B for a new instance (of subentity 2), save, swap object B for a new instance (of subentity 3), save

All these actions are serial (using "performSelector:onThread:withObject:waitUntilDone:YES in the case of running things on Thread 2). So there's no actual concurrency involved here, only MOC synchronisation.

The swapping actions above are performed by the same method, but naturally this method obtains its own copy of the A object against which it will make the changes. This is done by resolving the permanent ID of the A object we're playing with in the MOC being used exclusively for the thread. I have code that listens for NSManagedObjectContextDidSaveNotification on either MOC, and posts those changes to the other MOC, using - mergeChangesFromContextDidSaveNotification. Note this is done on the thread to which this notification is dispatched (the main thread), but I'm assured on this list that this is OK (i.e. - mergeChangesFromContextDidSaveNotification bends the rules about changing MOCs across threads).

The test reproduces the problem when Thread 1 attempts to make its first save. In this step, I log the object that A points to (which B) BEFORE I do the first swap, and AFTER this. I find that its BEFORE state is an instance of subentity 3 (so this look to be correctly propagated from the change on thread 2). After the swap, the logged output shows that this has been changed to an instance of subentity 2... then it is the subsequent save that fails.

So far all the things I have traced look right - the MOC instances for the two threads, the same instances in the NSManagedObjectContextDidSaveNotification, the right (opposite) instances being sent the mergeChangesFromContextDidSaveNotification message. Everything looks consistent - the MOC the original A is in, the strict correlation of this MOC to the thread, etc.

At the moment, I'm just not seeing how things can work up the point of failure, including the last change being (locally) correct, and then somehow the save deems that the relationship I've just checked on an object in that moc is suddenly nil.

I'm left wondering whether there's anything more I'm supposed to do to get a MOC properly synchronised with a set of changes from another, other than by sending it -mergeChangesFromContextDidSaveNotification with the changes send in the changed MOC's notification. I'm about to play with the merge policies on the MOCs to see if I can induce a change in behaviour (at least). At the moment, I have both my MOCs set up with NSMergeByPropertyObjectTrumpMergePolicy, which seemed reasonable given the description.

-- Luke




On 2009-09-29, at 8:16 AM, cocoa-dev-requ...@lists.apple.com wrote:

On Sep 28, 2009, at 17:20, Luke Evans wrote:

I have an entity (A) that has a to-one relationship with another very simple abstract entity (B), a kind of an enumeration, whose mere type represents the value. As a kind of enumeration value, the B entity has a fixed number of concrete subentities, with no properties, and the B entity has a single relationship property pointing back to A (i.e. this inverse relationship). The relationship from A to B is set as required (non-optional) as I want to make sure that any saved A, is defined properly with a particular B enumeration value which controls behaviour.

None of that is _probably_ relevant, but it provides a little background.

Now, I have some code that changes the value of the 'B enumeration value' that A is using. This does the following: 1. Create a new instance of the B subentity that represents the value we want (in the same MOC as A) 2. Delete the old B object that A was pointing to, i.e. [moc deleteObject:B]; 3. Set A's to-one relationship to point to the new B object (and for good measure, set B's inverse relationship - though this should be done automagically).
4. Save the moc

4. is where badness happens (failed to save). The error tells me that A's relationship property to B is nil... but just before I do the save I log the value of the object referenced by this relationship and it's the new 'B' object! I have no idea what I've done to upset Core Data such that it claims a relationship is nil when I save, but the line before the [moc save:&err], the relationship shows as referencing a perfectly good object.

From the Department of FWIW:

-- This sort of behavior is typical (in other contexts) of duplicate objects, the one you're looking at that's OK and the one you're not looking at that's causing the error.

-- I wonder if the B object is *really* in the same MOC as A, or if the new B object happens to be flagged as deleted.

-- If you're in a position to play around with the Core Data model, you could try making the relationship optional, and see if the save succeeds and what you get back the next time you fetch from the store.

-- What happens if you make B concrete? If you create a B instance instead of a sub-entity instance? (I have no reason to suspect abstract parent entities of malfunctioning, but they do seem to sit somewhat uneasily in the Core Data universe.)

-- Perhaps the save error means something other than what it appears to mean, or refers to a property other than the one it appears to reference.

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to