CoreData concurrency debug

I am using a database with NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)

Everything works fine, I save objects, read, and delete without problems.

If set "-com.apple.CoreData.ConcurrencyDebug 1" :

  • I save the object through the PerformAndWait block - ok
  • I do fetch - receive an object, but its properties are empty (data = fault) and it crashes. (EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0))

sample code:

public final class  DatabaseCore {

    private let persistentContainer: NSPersistentContainer
    private var backgroundContext: NSManagedObjectContext!
    private let queue: DispatchQueue

    public init() {
        self.persistentContainer = Self.createPersistentContainer()

        self.queue = queue
        self.queue.async {
            self.backgroundContext = Self.createNewBackgroundContext(container: self.persistentContainer)
        }

    // Private
    private static func createPersistentContainer() -> NSPersistentContainer {
        let model = NSManagedObjectModel(contentsOf: Bundle.module.url(forResource: "TestModel", withExtension: "momd")!) 
        let container = NSPersistentContainer(name: "TestModel", managedObjectModel: model)
        let description = NSPersistentStoreDescription()
        description.url = URL(fileURLWithPath: "/dev/null")
        container.persistentStoreDescriptions = [description]
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
               // print error
            }
        })
        return container
    }

     private static func createNewBackgroundContext(container: NSPersistentContainer) -> NSManagedObjectContext {
        let context = container.newBackgroundContext()
        context.mergePolicy = NSOverwriteMergePolicy
        context.undoManager = nil
        return context
    }

     private static func saveContext(_ context: NSManagedObjectContext) {

        context.performAndWait {
            if !context.hasChanges { return }
            do {
                try context.save()
            } catch {
                // print error
            }
        }
    }

    // Public
    public func upsert(_ block: @escaping (NSManagedObjectContext) -> Void, completion: (() -> Void)?) {

        self.queue.async {
            let context: NSManagedObjectContext! = self.backgroundContext
            self.backgroundContext.performAndWait {
                block(context)
                Self.saveContext(context)
            }
            completion?()
        }
    }

    public func fetch<ResultType: NSFetchRequestResult>(_ request: NSFetchRequest<ResultType>, completion: @escaping ([ResultType]) -> Void) {

        self.queue.async {
            var result: [ResultType] = []

            self.backgroundContext.performAndWait {
                do {
                    result = try request.execute()
                } catch let error {
                    // print error
                }
            }
            completion(result)
        }
    }
}

I set a breakpoint to result fetch func

if set  "-com.apple.CoreData.ConcurrencyDebug 1":

(lldb) po record

<Test: 0x7b1400030070> (entity: Test; id: 0xbb200e8d680f1045 <x-coredata://1BFC3A95-3F7D-4A23-AD20-FDF23B575D73/CDLog/p1>; data: <fault>)

(lldb) po record.value

error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).

The process has been returned to the state before expression evaluation.

Without  "-com.apple.CoreData.ConcurrencyDebug 1", everything works correctly, without a crash.

(lldb) po record

<Test: 0x7b1400028c80> (entity: Test; id: 0xb29880bd32a57757 <x-coredata://1BFC3A95-3F7D-4A23-AD20-FDF23B575D73/CDLog/p1>; data: <fault>)

(lldb) po record.value

0

How can this be fixed? Thank you! =)

Xcode 13.3

The debugger is working! ;-)

Managed objects should never cross thread boundaries, and accessing attributes from a managed object outside of the context in which is was created or fetched can cause crashes.

The call to completion(result) should be within the the self.backgroundContext.performAndWait block.

CoreData concurrency debug
 
 
Q