This is on a background thread. It's a private concurrency type context and it's performed within context performBlock. I'm doing it right, at least I think I am.
I'm not a beginner to core data, however I could be missing something obvious - as I've been staring at this a while.
Here is the code:
FFXCollection *backgroundCollection = (FFXCollection *) [context objectWithID:collectionID];
//At this point backgroundCollection.managedObjectContext is the same as context
NSError *error = nil;
[context save:&error];
NSLog(#"error %#", error); //Note that there is no error here
if (!backgroundCollection.managedObjectContext) {
DLog(#"why not?"); //At this point the managedObjectContext is nil!!!
}
Why is the managedObjectContext becoming nil inside the managed object?
The example is contrived, but demonstrates my problem. In my actually code, a save occurs and then i'm trying to set up a relationship. Then when another save occurs I get a validation error because of the above.
Thanks
Use existingObjectWithID instead.
existingObjectWithID:error: Returns the object for the specified ID.
(NSManagedObject *)existingObjectWithID:(NSManagedObjectID )objectID error:(NSError *)error Parameters objectID The object ID for the requested object. error If there is a problem in retrieving
the object specified by objectID, upon return contains an error that
describes the problem. Return Value The object specified by objectID.
If the object cannot be fetched, or does not exist, or cannot be
faulted, it returns nil.
Discussion
If there is a managed object with the given ID already registered in the context, that object is returned directly; otherwise the
corresponding object is faulted into the context.
This method might perform I/O if the data is uncached.
Unlike objectWithID:, this method never returns a fault.
Availability Available in OS X v10.6 and later. See Also –
objectWithID: – objectRegisteredForID: Declared In
NSManagedObjectContext.h
Related
I'm really struggling with this issue:
I have an object saved in core data. I have a method which updates some properties of that object. I'm using that object's Managed Object Context from obj.managedObjectContext.
I change 2 properties: obj.pending = YES and obj.body = 'text'.
I then save the object back to Core Data as follows:
if ([managedObjectContext save:&saveError]) {
[persistenceController saveContextAndWait:YES completion:^(NSError *error) {
[OfflineManager postData];
}];
}
The postData method gets all objects from Core Data where the obj.pending == YES and posts the data. It's managedObjectContext is of type NSPrivateQueueConcurrencyType.
My issue is as follows: The obj.pending seems to be updating but the obj.body is not. If I add a breakpoint to the app before it posts and restart the app, then the obj.body is correctly updated.
If this is a context issue why are some of the object properties' changes being seen and other not?
I've been hacking away at this for hours, any help would be most grateful.
Thanks
Thanks #Avi for the solution. The issue was with the persistent context and not having a merge handler to handle background contexts. My solution was to just create a new context every time for the postData method.
I'm trying to perform a fetch to retrieve managed objects from the context using an array of object IDs I gathered from a separate context. However, the fetch comes back with an empty array.
From the "Retrieving Specific Objects" section of the "Core Data Programming Guide"
link:
If your application uses multiple contexts and you want to test whether an object has been deleted from a persistent store, you can create a fetch request with a predicate of the form self == %#. The object you pass in as the variable can be either a managed object or a managed object ID..."
If you need to test for the existence of several objects, it is more efficient to use the IN operator than it is to execute multiple fetches for individual objects, for example:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self IN %#",
arrayOfManagedObjectIDs];
While this is talking about testing for object deletion, I presumed if the objects are not deleted then the results array is not empty, and will contain the actual NSManagedObjects. However, when I execute this code:
- (NSArray *)managedObjectsOfEntityName:(NSString *)entityName fromObjectIDs:(NSArray *)objectIDs managedObjectContext:(NSManagedObjectContext *)managedObjectContext
{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
request.predicate = [NSPredicate predicateWithFormat:#"self IN %#", objectIDs];
__autoreleasing NSError *error = nil;
NSManagedObjectID *testID = objectIDs[0];
NSManagedObject *obj = [managedObjectContext existingObjectWithID:testID error:&error];
if (!obj)
{
NSLog(#"Unable to perform fetch. Error: %#", error);
}
error = nil;
NSArray *results = [managedObjectContext executeFetchRequest:request error:&error];
if (!results)
{
NSLog(#"Unable to perform fetch. Error: %#", error);
}
return results;
}
The results array is non-nil and empty, while obj is properly populated.
I've added the explicit call to existingObjectWithID: as a sanity check, and it comes back with the expected object, without error.
Here's the debugger output for the pertinent variables:
(lldb) po entityName
Foo
(lldb) po objectIDs
<__NSArrayI 0x1170d4950>(
0xd0000000055c0082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p343>,
0xd000000008e80082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p570>,
0xd000000008840082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p545>,
0xd000000006040082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p385>,
0xd000000007740082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p477>,
0xd000000008280082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p522>,
0xd000000008e40082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p569>
)
(lldb) po request
<NSFetchRequest: 0x10f338840> (entity: Foo; predicate: (SELF IN {
0xd0000000055c0082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p343>,
0xd000000008e80082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p570>,
0xd000000008840082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p545>,
0xd000000006040082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p385>,
0xd000000007740082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p477>,
0xd000000008280082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p522>,
0xd000000008e40082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p569>});
sortDescriptors: ((null)); type: NSManagedObjectResultType; )
(lldb) po request.predicate
SELF IN {
0xd0000000055c0082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p343>,
0xd000000008e80082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p570>,
0xd000000008840082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p545>,
0xd000000006040082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p385>,
0xd000000007740082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p477>,
0xd000000008280082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p522>,
0xd000000008e40082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p569>}
(lldb) po testID
0xd0000000055c0082 <x-coredata://CEB1EDA5-7F7A-4342-85E5-6C2E261308CC/Foo/p343>
(lldb) p testID.isTemporaryID
(bool) $7 = false
(lldb) p obj
(NSManagedObject *) $9 = 0x000000010f338630
(lldb) po results
<__NSArrayI 0x10f205c70>(
)
(lldb) po testID.entity
nil
The nil entity on the testID is strange, but I'm not sure that's "bad."
So, I'm obviously at a loss as to why the fetch is coming back empty. The entity name is correct, the fetch and predicate look good, and the object is in the context, but still zero results.
Additional Context:
Essentially, the larger picture is that I have a background operation which uses its own Managed Object Context (MOC) to perform some work. The results of that work are needed on the main queue, so I package up the objectIDs resulting from the work and pass those to the main queue. On the main queue I need the actual managed objects back, so I'm trying to fetch them, by objectID, from the main queue's MOC. I realize I can use objectWithID: or existingObjectWithID:error: or even objectRegisteredForID: on the MOC to get these objects, but each have their own special issues:
objectWithID: might return an object which is bogus if the object is not in the context, and if it is in the context it will return a fault.
existingObjectWithID:error: is great, since we'll get back nil (rather than a bogus object) but it too returns a fault.
objectRegisteredForID: will return nil if the object is not in the context already.
So, if I use either objectWithID: or existingObjectWithID:error: in a loop to get a bunch of objects, that's potentially n trips to the persisted store to fault the objects, meaning performance is going to be potentially awful. If I use objectRegisteredForID: I might not get the object at all if it happens to not already be in the main queue's MOC.
So, rather than try to iterate over the array, I expected a fetch request would limit the overhead of interacting with the persistance store and return all the objects I needed in one fell swoop.
Addition:
As an aside, the issue really feels like it has to do with the #"self IN %#" predicate, since I can remove that predicate and fetch all objects of entityName without issue. I have also tried #"objectID IN %#" as the predicate with the same (zero) results.
Okay, so ready for a trip down the Core Data rabbit hole?
TL;DR
NSManagedObjectIDs whose persistent store coordinator is no longer in memory lose their NSEntityDescription (entity) and do not equate (isEqual: returns NO) to NSManagedObjectIDs from a different persistent store coordinator even though their URIRepresentation is the same.
Down the Rabbit Hole
Sweet... here we go.
Remember, I'm gathering the array of objectIDs from a separate Managed Object Context (MOC) on a separate thread? Good. I didn't mention, however, that that MOC is using its own Persistent Store Coordinator (PSC) (pointing at the same file, of course). That PSC and MOC are short lived since once the work is done they are autoreleased, but while they are alive, I save off instances of NSManagedObjectID into an NSArray (which gets passed to a different thread).
Once received in the separate thread, the NSManagedObjectIDs have a nil entity. This seems to be happening (educated guess here...) because the PSC from which these objectIDs came from is now no longer in memory and the NSManagedObjectID must keep a week reference to the NSEntityDescription (entity) which must be held by the PSC. The nil entity seems to cause issues, as commenters suspected...
I can't be sure of the internals, but it would appear that without the entity the NSManagedObjectID is not equal to another NSManagedObjectID with the same URIRepresentation. Let me illustrate:
In the following code:
* objectID is an NSManagedObjectID * from a persistant store which is no longer in memory and whose entity property is nil.
* managedObjectContext is our MOC on our current thread (of course)
NSURL *URIRep = objectID.URIRepresentation;
NSManagedObjectID *newObjectID = [managedObjectContext.persistentStoreCoordinator managedObjectIDForURIRepresentation:URIRep];
If we look at this in the debugger... guess what:
(lldb) p [ojectID isEqual:newObjectID]
(bool) $0 = false
Even though the URIRepresentation of these two objectIDs must be the same, the objects do not equate.
This is almost certainly why the predicate [NSPredicate predicateWithFormat:#"self IN %#", objectIDs] is failing to match any objects and causing the fetch request to return zero objects.
Interestingly, however, in the following code, where testID is an NSManagedObjectID * from a persistant store which is no longer in memory and whose entity property is nil, the object is retrieved from the MOC without issue:
NSManagedObject *obj = [managedObjectContext existingObjectWithID:testID error:&error];
Workaround
There are two things which I've verified as a workaround for this:
Use the same Persistent Store Coordinator (PSC) when communicating NSManagedObjectIDs between contexts (and make sure it stays resident in memory). If this is done, the NSManagedObjectIDs never lose their entity and everything works as expected.
Instead of passing NSManagedObjectIDs around from context to context, use their URIRepresentation. i.e. instead of passing an array of NSManagedObjectID objects, pass an array of NSURLs representing the URIRepresentation of each NSManagedObjectID. Once ready to perform a fetch with those objectIDs use the managedObjectIDForURIRepresentation: message to get a NSManagedObjectID from the current MOC's PSC.
Option 1 is simpler, but might have some disadvantages with concurrency timing and may be too restrictive if, say, you wanted to have a separate PSC for your operations which was read only, for instance.
In my (relatively limited) experience calling managedObjectIDForURIRepresentation: it appears to be very performant, so converting URIRepresentation NSURLs to NSManagedObjectIDs doesn't seem to add that much overhead.
Thanks
Thanks for following along... I hope this helps explain the issue and workarounds clearly. I certainly feel like I earned a merit badge in Core Data object IDs and persistent store coordinators by digging through all this.
Cheers,
Levi
I’m having a Core Data issue while using 2 NSManagedObjectContext, running on different threads, and migrating changes from the parent to the child. In essence, I’m able to pull the changes from parent to child, but immediately after doing so, the changes are lost.
The app I’m building is a test for syncing a model between multiple devices and a server.
The context that holds the objects the user interacts with is on the main thread and is configured as a child of the sync context and is created like this (error checks omitted)
NSManagedObjectContext *parentMOC = self.syncManagedObjectContext;
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext performBlockAndWait:^() {
[_managedObjectContext setParentContext:parentMOC];
}];
The syncManagedObjectContext is the parent context and is where the syncManager performs the sync with the server. It gathers up objects modified by the user, sends the changes to the server and merges changes received back. The syncManagedObjectContext also sends its data to the PersistentStoreCoordinator to be stored in SQLite.The context runs on a background “thread” so that syncing and storing does not block the main thread. Here’s how it is created:
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
_syncManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_syncManagedObjectContext performBlockAndWait:^(){
[_syncManagedObjectContext setPersistentStoreCoordinator:coordinator];
}];
Sync Logic Flow
The syncing is kicked off when the syncManager handles the NSManagedObjectContextObjectsDidChangeNotification from the main context. Here is a rough flow of what happens:
syncManager handles NSManagedObjectContextObjectsDidChangeNotification which lets it know that objects have been changed in the main thread context. It calls save on the main context which saves the changes to syncMOC.
When the syncManager receives NSManagedObjectContextDidSaveNotification to indicate the save has been completed, it gathers up the newly changed objects from the sync context and sends the changes to the server. It then does a save on the sync MOC which sends the data to SQLite. Note that each object has a uuid field which I create as a portable id - not be confused with Core Data’s objectID, as well as a lastSynced timestamp that is provided by the server.
The server responds back with updated timestamps for the objects sent, as well as any other changes that have happened. In the simplest case that illustrates the issue, what is received is a set of records that consists of the uuid and the updated lastSynced time for the objects that the syncManager just sent.
For each update, the syncManager updates the object in the syncContext and stores the NSManagedObject objectID for the object (not the uuid) in an array.
The syncManager then does a save on the on the sync MOC to write the data to disk and posts a message to provide the main MOC with the array of objectID’s for updated objects. At this point, if I do a fetch for all Entities in the syncMOC and dump them to the log, they all have the correct values. Further, if I look at the SQLite database on disk, it too has the correct values.
Here’s abbreviated code (some error checking and non-essential stuff removed) for how the updates are merged in on the main thread, with comments as to what’s happening: (Note: I’ve been careful in the code to use performBlock and it appears from tracing in the debugger that everything is happening on the correct thread.)
-(void)syncUpdatedObjects: (NSNotification *)notification
{
NSDictionary *userInfo = notification.userInfo;
NSArray *updates = [userInfo objectForKey:#"updates"];
NSManagedObjectContext *ctx = self.managedObjectContext;
[ctx performBlock:^() {
NSError *error = nil;
for (NSManagedObjectID *objID in updates) {
NSManagedObject *o = [ctx existingObjectWithID:objID error:&error];
// if I print out o or inspect in the debugger, it has the correct, updated values.
if (o) {
[ctx refreshObject:o mergeChanges:YES];
// refreshObject causes o to be replaced by a fault, though the NSLog statement will pull it back.
// NOTE: I’ve used mergeChanges:NO and it doesn’t matter
NSLog(#"uuid=%#, lastSynced = %#", [o valueForKey:#"uuid”], [o valueForKey:#"lastSynced"]);
// Output: uuid=B689F28F-60DA-4E78-9841-1B932204C882, lastSynced = 2014-01-15 05:36:21 +0000
// This is correct. The object has been updated with the lastSynced value from the server.
}
}
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#“MyItem"
inManagedObjectContext:ctx];
request.entity = entity;
NSArray *results = [ctx executeFetchRequest:request error:&error];
for (MyItem *item in results)
NSLog(#"Item uuid %# lastSynced %# ", item.uuid, item.lastSynced);
// Output: uuid B689F28F-60DA-4E78-9841-1B932204C882 lastSynced 1970-01-01 00:00:00 +0000
// Now the objects have incorrect values!
}];
}
In case you missed it, the issue is there in the comments after the NSLog statements. The object initially has the correct values from the parent context, but then they become incorrect. Look at the timestamp, specifically.
Does anyone have any idea why this would happen? I should note that the business of doing the fetch at the end was part of debugging. I was noticing that the NSManagedObjects being held on to in the program did not have the correct values, even though I was seeing that things were updated correctly in the above code and through uniquing, they should be updated too. I thought that what might be happening is that I was creating “extra” objects with the correct values while the old ones were still around. However, the fetch showed that the right objects and only the right ones were around, only with bad values.
One more thing, if I do the same fetch in the parent context after this function runs, it shows the correct values as does SQLite.
Any help is much appreciated!
I finally found the answer to this issue and hope it might help others.
What I noticed at some point is that the object ID coming back to the main context had incorrect
Core Data ID - they should have been permanent, but were not. And in fact, during the merge, I
realized that the ID for a given object in my main MOC and the ID for the changes to merge for
that object were both temporary, but different. But neither were the permanent ID that they should have been.
A search on Stack Overflow for that issue led me to
this answer https://stackoverflow.com/a/11996957/1892468 which gives a workaround for a known Core
Data bug.
So the problem wasn't that I was doing it wrong, it was that Core Data was not doing what it
said it would do. The workaround is that during the save operation on the main object context I added the following
code prior to calling save.
if ctx.insertedObjects.count > 0 {
do {
try ctx.obtainPermanentIDsForObjects(Array(ctx.insertedObjects))
} catch {
log.error("Unable toobtain permanent ids for inserts")
}
}
That fixed it! So what I had originally been observing was that the merge was not actually taking
place. There were 2 objects alive for what was supposed to be one.
You could simply subscribe to NSManagedObjectContextDidSaveNotification of the sync context and then merge changes into UI context by calling -mergeChangesFromContextDidSaveNotification:.
My iOS app uses core data via multiple threads. I am getting some crash reports with the following message: "'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x1e07a9b0 ''
I understand what is causing this problem - that the object was deleted but another thread is trying to access it. I am working to solve the problem but I want to add a check in the background thread to see if the object will fault in this manner.
My code at the moment relates to myObject.myValue. Is it possible to do some check, such as:
if (!myObject.myValue) {
return;
}
... so that it will get out of the method before doing anything that could cause such a crash? Or will simply calling myObject.myValue, even to see if it's null, cause such an exception to be thrown?
You could try and use existingObjectWithID:error::
Returns the object for the specified ID.
- (NSManagedObject *)existingObjectWithID:(NSManagedObjectID *)objectID error:(NSError **)error
Discussion
If there is a managed object with the given ID already registered in the context, that object is returned directly; otherwise the corresponding object is faulted into the context.
This method might perform I/O if the data is uncached.
Unlike objectWithID:, this method never returns a fault.
You could dO:
if ([myMOC existingObjectWithID:myObject.objectID error:&error])
...
You should verify that the object exists before accessing it's variables if you're having issues where the object may be deleted on another thread.
Two methods:
Refresh the view datasources whenever your data is being deleted. You can do this by registering for the NSManagedObjectContextObjectsDidChangeNotification notification and then parsing the userInfo on that notification to see which object was deleted.
Use code similar to below when you're passing data around to multiple threads.
Example:
// Cache and pass the object's ID off to another thread to do work on
// You can just store it as a property on the class
#try {
NSManagedObject *theObject = [managedObjectContext objectWithID:self.theObjectID];
// do stuff with object
}
#catch (NSException * e) {
// An entity with that object ID could not be found (maybe they were deleted)
NSLog(#"Error finding object: %#: %#", [e name], [e reason]);
}
You can check the NSManagedContext is existed when you use the NSManagedObject.
like this:
if (obj.managedObjectContext)
{
//do things
}
You can check [myObject isFault] where myObject is a NSManagedObject instance
You could give a try to use :
shouldDeleteInaccessibleFaults
property on managed object context. As this article says it should change the behaviour of faulting already deleted object.
https://cocoacasts.com/what-are-core-data-query-generations/
Edit:
Since iOS 9 (when it was added) this property default value is YES.
I am using a UIManagedDocument for core data. I have Calendar objects in my managedObjectContext that have a calendarSelected attribute. The Calendar entity has a to-many relationship to CalendarEntry.
When I change their calendarSelected attribute and then perform a NSFetchRequest to get CalendarEntry objects with the following predicate:
[NSPredicate predicateWithFormat:#"calendar.calendarSelected = YES"]
the calendar.calendarSelected does not seem to be seeing the change I made without me calling
[myManagedDocument saveToURL:myManagedDocument.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:^(BOOL success) {}];
first. I read somewhere that fetching things from the same context should honor changes made to in that context even if the changes had not been written to the persistent store. What am I missing?
Update:
It appears to be happening when the calendarEvents relationships is a fault: calendarEvents = "<relationship fault: 0x91aec90 'calendarEvents'>"; but works when the relationship is not a fault.
If the issue occurs only on newly-inserted CalendarEntry objects pointed to by the to-many relationship, then it's possible that they don't yet have permanent object ids. This is easy to debug via just dumping the object id and checking to see if it's temporary or permanent.
I've seen this happen when the containing object in the to-many relationship is retained; it seems that so long as it is retained, the to-many contained objects in the relationship never get to a point wherein they obtain permanent ids. Somewhat easy to debug by putting the application in the background and restarting it; the backgrounding will typically force the UIManagedDocument to save, and things will start working as you expect thereafter, as the CalendarEntry entities will have been assigned permanent ids and thus will become fetchable.
So far as saving the UIManagedDocument, you don't have control over when that'll happen when using a UIManagedDocument. The best thing to do is schedule a save to occur in the future, via updateChangeCount:UIDocumentChangeDone, but again, it'll happen 'soon', but not deterministically, i.e., it's not possible to know when it'll happen.
To resolve the temporary vs. permanent object id issue, if that's what you're seeing, try the following category on NSManagedObjectContext; call it after you've completed inserting new CalendarEntry objects, in order to force the issue.
// Obtain permanent ids for any objects that have been inserted into
// this context.
//
// As of this writing (iOS 5.1), we need to manually obtain permanent
// ids for any inserted objects prior to a save being performed, or any
// objects that are currently retained, that have relationships to the
// inserted objects, won't be updated with permanent ids for the related
// objects when a save is performed unless the retained object is refetched.
// For many objects, that will never occur, so we need to force the issue.
- (BOOL)obtainPermanentIDsForInsertedObjects:(NSError **)error
{
NSSet * inserts = [self insertedObjects];
if ([inserts count] == 0) return YES;
return [self obtainPermanentIDsForObjects:[inserts allObjects]
error:error];
}
Basically, after making any changes, you must either use an undo manager, or call [document updateChangeCount:UIDocumentChangeDone]; and THAT'S ALL! Any other "save" calls will just break something else down the line somewhere.
The canonical method of updating should be...
You know you are on main thread
NSManagedObjectContext *ctx = document.managedObjectContext;
// Do stuff with it
[document updateChangeCount:UIDocumentChangeDone];
You are on a different thread
[document.managedObjectContext performBlock:^{
// Do stuff with it on the main thread
[document updateChangeCount:UIDocumentChangeDone];
}];
OR
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
moc.parentContext = document.managedObjectContext;
[moc performBlock:^{
// Do stuff with it on a background thread
// Push changes up to parent (which is the document's main MOC)
// The document will know it's dirty and save changes
[moc save:&error];
}];
OR if you want to make changes in a background thread, without messing with the main document MOC, do them on the parent... the main MOC will not see them until the next fetch.
[document.parentContext.managedObjectContext performBlock:^{
// Do stuff with it on a background thread, but the main MOC does not
// see the changes until it does a fetch.
}];