NSManagedObject: create on separate thread
Asked Answered
R

1

5

I understand that Core Data is not thread safe and that NSManagedObjectContext and NSManagedObjects associated with a context cannot be passed from thread to thread.

Question:

However, if I have a NSManagedObjectContext on my main thread, can I create a NSManagedObject object on a background thread (WITHOUT attaching it to any context -- that is, simply call alloc/init on NSManagedObject), then pass that NSManagedObject back to the main thread and add it to the context from there? I've reviewed the docs on Core Data concurrency, but can't find anything that says this usage pattern is okay.

Context:

I have a background thread that performs a complex task and then publishes a result. The result is an NSManagedObject subclass that contains a few attributes: time, a file path, and a success or error message (as a string). I want to create the result object on the background thread, then toss it back to the main thread and add it to the Core Data context where it will be displayed in a tableView.

If I can't create the managedObject on the background thread, then I'll need to create a dictionary, pass the dictionary to the main thread, read the keys, create the managedObject from those values, etc. Just seems cleaner to create the managedObject on the background thread if possible.

Resection answered 7/7, 2011 at 17:9 Comment(0)
S
7

The better approach is to have a context per thread. This way each thread will have its own scratch pad to play around with. Then when you background thread finishes, tell your main thread to update its view or ui table view or how every you are presenting your data.

You'll need to notify your main thread when changes occur. The big problem is, the contexts between different threads and main thread are not aware of each other. There is a way in core data to keep the contexts in sync. When you want to save, the context on the background thread should broadcast an NSManagedObjectContextDidSaveNotification notification.

So for example, in your main method in your NSOperation, you could do this:

NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 
[nc addObserver:self
       selector:@selector(mergeChanges:)
           name:NSManagedObjectContextDidSaveNotification
         object:context];

the mergeChanges would be a private method in your NSOperation instance.

example of merge changes

- (void)mergeChanges:(NSNotification *)notification
{
    ApplicationController *appController = [[NSApplication sharedApplication] delegate];
    NSManagedObjectContext *mainContext = [appController managedObjectContext];

    // Merge changes into the main context on the main thread
    [mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
                                  withObject:notification
                               waitUntilDone:YES];  
}
Scaphoid answered 7/7, 2011 at 17:43 Comment(2)
@Doug Yes, I am aware of how to use Core Data in a multi-threaded environment. My question was can I alloc/init an NSManagedObject on any thread and then add it to the context associated with the main thread? In other words, if I create an NSManagedObject WITHOUT using -insertNewObjectForEntityForName:InManagedObjectContext:, can I use NSManagedContext's addObject: method on the main thread to add a managedObject alloc/init-ed on a background thread? The extra overhead of the background context associated with the approach above is substantial for large, complex object graphs.Resection
Although an init will create an NSManagedObject, it is in an invalid state. Objective-C does not enforce the use of designated initializers, but you must use them anyway if you want an object in a valid state. For instances of NSManagedObject, that designated initializer is '-insertNewObjectForEntityForName:InManagedObjectContext:'Ginder

© 2022 - 2024 — McMap. All rights reserved.