Making Com.Apple.Coredata.Concurrencydebug 1 Work

Making com.apple.CoreData.ConcurrencyDebug 1 work

The argument -com.apple.CoreData.ConcurrencyDebug is available from iOS > 8 and OSX > 10.10 : make sure you target the right platform.

The console should log this if the flag is enabled :

CoreData: annotation: Core Data multi-threading assertions enabled.

Source : Core Data Concurrency Debugging from Ole Begemann

Using -com.apple.CoreData.ConcurrencyDebug 1 on async block crash on both MainContext and PrivateContext

That's not how Core Data queue concurrency works. When using either NSPrivateQueueConcurrencyType or NSMainQueueConcurrencyType, you need to use the performBlock or performBlockAndWait methods on NSManagedObjectContext whenever you access the context. The fact that you're using GCD here is irrelevant; if you're using queue concurrency, you must use those methods, or you're doing it wrong.

You need to put your fetchUserWithContext call inside a block passed to one of those methods. You also need to put anything else that accesses objects fetched from the context inside a block passed to one of those methods.

Core Data Concurrency Debugging

Make sure you make all UI changes in the main thread.

ambiguous for type lookup in this context when -com.apple.CoreData.ConcurrencyDebug 1 is active

When concurrency debugging is on, the app will crash any time you break the concurrency rules. Breaking the rules on its own doesn't always crash the app-- but with debugging enabled, you're saying that you want to crash as soon as you break the rules, even if the app would work normally without debugging. This is a good thing, because breaking the rules will probably make the app crash eventually even if it doesn't happen right now.

How you're breaking the rules here is:

  • You're creating a new private queue context with newPrivateContext.
  • You're using that context without calling perform or performAndWait.

With a private queue context, you must use one of those functions whenever you use the context. Really the only time you don't have to use one of those is if you're using main queue concurrency and you know that your code is running on the main queue. You can sometimes get away with not doing that, if everything is just right, but concurrency debugging will stop you immediately. That's what you're seeing.

Why does the raywenderlich code break if you enable the scheme -com. apple.CoreData.ConcurrencyDebug 1

This is a good catch. I wish the answer helped more though.

I downloaded the code and tried it out, and after messing with it for a bit I'm pretty sure that this is a bug in Apple's frameworks. It looks like the code in the sample project is correct, but the concurrency check flags it anyway. I don't know if this is because the code actually causes a concurrency violation or if there's no violation but the flag is buggy.

What you're seeing is unfortunately just what happens right now, and it's not fixable in an app-- only Apple can resolve it. It's also a longstanding issue, as you can see from this answer from a few years ago. I suggest filing a bug with Apple.

How does Core data concurrency work with multithreading in Swift 3?

Core data in general is not multithreading friendly. To use it on concurrent thread I can assume only bad things will happen. You may not simply manipulate managed objects outside the thread on which the context is.

As you already mentioned you need a separate context per thread which will work in most cases but by my experience you only need one background context which is read-write and a single main thread read-only context that is used for fetch result controllers or other instant fetches.

Think of a context as some in-memory module that communicates with the database (a file). Fetched entities are shared within the context but are not shared between contexts. So you can modify pretty much anything inside the context but that will not show in the database or other contexts until you save the context into the database. And if you modify the same entity on 2 contexts and then save them you will get a conflict which should be resolved by you.

All of these then make quite a mess in the code logic and so multiple contexts seem like something to avoid. What I do is create a background context and then do all of the operations on that context. Context has a method perform which will execute the code on its own thread which is not main (for background context) and this thread is serial.

So for instance when doing a smart client I will get a response from server with new entries. These are parsed on the fly and I perform a block on context to get all the corresponding objects in the database and create the ones that do not exist. Then copy the data and save the context into database.

For the UI part I do similar. Once an entry should be saved I either create or update the entity on the background context thread. Then usually do some UI stuff on completion so I have a method:

    public func performBlockOnBackgroundContextAndReturnOnMain(block: @escaping (() -> Void), main: @escaping (() -> Void)) {
if let context = context {
context.perform {
block()
DispatchQueue.main.async(execute: { () -> Void in
main()
})
}
}
}

So pretty much all of the core data logic happens on a single thread which is in background. For some cases I do use a main context to get items from fetch result controller for instance; I display a list of objects with it and once user selects one of the items I refetch that item from the background context and use that one in the user interface and to modify it.

But even that may give you trouble as some properties may be loaded lazily from database so you must ensure that all the data you need will be loaded on the context and you may access them on the main thread. There is method for that but I rather use wrappers:

I have a single superclass for all the entities in the database model which include id only. So I also have a superclass wrapper which has all the logic to work with the rest of wrappers. What I am left with in the end is that for each of the subclass I need to override 2 mapping methods (from and to) managed object.

It might seem silly to create additional wrappers and to copy the data into memory from managed object but the thing is you need to do that for most of the managed objects anyway; Converting NSData to/from UIImage, NSDate to/from Date, enumerations to/from integers or strings... So in the end you are more or less just left with strings that are copied 1-to-1. Also this makes it easy to have the code that maps the response from your server in this class or any additional logic where you will have no naming conflicts with managed objects.



Related Topics



Leave a reply



Submit