4

My understanding of threads with respect to an NSManagedObjectContext is that it can only execute core data fetch requests, deletes, etc., on the thread from which it was created. Is there any way to check to see what thread an NSManagedObjectContext was created on, or if at a particular point of execution the current thread is that of a particular NSManagedObjectContext?

Thanks!

Mason
  • 6,893
  • 15
  • 71
  • 115

3 Answers3

2

My understanding of threads with respect to an NSManagedObjectContext is that it can only execute core data fetch requests, deletes, etc., on the thread from which it was created.

That's not really accurate. It would be better to say that contexts cannot be used concurrently by more than one thread or queue. A common approach to dealing with this is to create different contexts for each thread/queue. It's also possible to use the performBlock and performBlockAndWait methods to use contexts on multiple threads while keeping context access effectively single-threaded.

As a result, contexts don't have any notion of belonging to a thread or queue, nor do threads have any reference to contexts that were created on them.

If you follow the approach of one context per thread or queue, you need to keep track of where code will run and use the appropriate context. For example when using GCD, create a context for a specific dispatch queue, and only use it when you've used something like dispatch_async to run on that queue.

If you really want to link a context with a queue, you could use your own data structure to look up contexts from whatever concurrency scheme you're using-- by the current NSOperationQueue, or dispatch queue, or NSThread, or whatever. This is rarely needed, but it's a possibility if you can't find better techniques.

Tom Harrington
  • 69,312
  • 10
  • 146
  • 170
  • I didn't actually need to implement this, I was just curious. Thanks for such a thorough answer! – Mason Aug 25 '13 at 01:57
1

As far as I know you can't. At least not too easily. Why? Use -performBlock: - it will perform all requests on the correct thread.

Mario
  • 4,530
  • 1
  • 21
  • 32
1

Sorry Tom Harrington, but that's actually not at all correct. Although you can technically do this, the results will be random and usually (in my experience) result in race conditions that become extremely difficult to debug.

The DOCs explicitly indicate that you should use a context PER thread. In fact even some of the best frameworks around (i.e. MagicalRecord) behave in this way. NSManagedObject's and their contexts are NOT thread safe, however objectIDs are.

To check for changes, you could either persist your changes up to the parent context's or you can listen to the notifications provided. Using the second method, you'll then need read the objectIDs of the item(s) you want to access, then request them again from your local context.

Read the following Apple Documentation to better understand how this works.

https://developer.apple.com/library/ios/documentation/cocoa/conceptual/coredata/Articles/cdConcurrency.html


After further research I have found a document last updated over the past few weeks and although you are correct in regards to the performBlock methods it does still state that you should not pass contexts between threads. Perhaps I misread the question and responded a little quickly. I've recently been working on a large CoreData based application and I know we have had a lot of issues related to contexts and threads, so I responded a little quick. ;)

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/CoreDataFramework/Classes/NSManagedObjectContext_Class/NSManagedObjectContext.html

Shaps
  • 91
  • 8
  • I'm not sure what you are saying is wrong with Tom's post. Furthermore, your statement `The DOCs explicitly indicate that you should use a context PER thread` is inaccurate. At best, you are referring to part of out-of-date documentation related to the confinement pattern. Most of the recent documentation and advice directly from Apple suggests ignoring threads all together and use `performBlock` to interact with managed object contexts. – Jody Hagins Mar 03 '14 at 21:14
  • You should use the new concurrency initialisers along with performBlock, that is true. However those new APIs only solve the issues related to merging across contexts. They don't suddenly make contexts, or even NSManagedObject's thread safe. So its important to still use only 1 context per thread and then as you suggest, use performBlock to save changes. – Shaps Mar 08 '14 at 01:07
  • Maybe I am misunderstanding your point. The new APIs *do* make using MOCs thread-safe... in the same way that using any synchronization API makes using objects thread-safe. If you use the confinement API, you need to be concerned about which thread you are on. Otherwise, you should completely forget about threads, and wrap every access inside one of the `performBlock` methods because that is the synchronization API provided by core data. `performBlock` is not just for saving and merging. – Jody Hagins Mar 09 '14 at 13:52
  • Exactly.. to be honest, I think I lost track of my point haha.. but yes, that's what I was trying to say, to ensure that all access is done inside performBlock otherwise you can end up with tread-safety issues. ;) – Shaps Mar 20 '14 at 13:51