kerry
kerry

Reputation: 2562

CoreData crashing while reading the fetched object property from the same thread

Below, I have 3 snippets of code related to CoreData fetching objects in different threads in different ways. One of these ways is crashing with EXC_BAD_INSTRUCTION when I am trying to read the data after fetching it from DB even though the fetching and reading is being done on the same thread.

print("hello: current thread is \(Thread.current)")
let moc = self.getChildMoc()
moc.performAndWait {
    let contacts = PPContactSyncHelper.contactsIfExistsWith(connectIds: connectIds, moc: moc)
    contacts.forEach { contact in
        print("hello: 2. current thread is \(Thread.current)")
        print("hello: \(contact.connectId)")
    }
}
        
DispatchQueue.main.async {
    let abContacts = PPContactSyncHelper.contactsIfExistsWith(connectIds: connectIds, moc: self.mainContext)
    abContacts.forEach { abContact in
        print("hello: \(abContact.connectId)")
    }
}
        
let contacts = PPContactSyncHelper.contactsIfExistsWith(connectIds: connectIds,
                                                                moc: moc)
contacts.forEach { contact in
    print("hello: 2. current thread is \(Thread.current)")
    print("hello: \(contact.connectId)")
}

The last snippet is the one that causes the issue while others can read the data successfully.

This is what I am doing.

  1. I create a new child context with type privateQueueConcurrencyType and parent set as mainContext
  2. I use this context first using performAndWait to fetch and read the data which works correctly.
  3. I then try to fetch and read in main thread using mainContext. That also works.
  4. When I try to fetch using the child context on the same thread and read without perform block, there it crashes even though I am on the same thread.

The function PPContactSyncHelper.contactsIfExistsWith fetches the data from coredata inside the performAndWait block using the context provided.

What am I missing here?

Upvotes: 2

Views: 916

Answers (2)

Tom Harrington
Tom Harrington

Reputation: 70936

With managed object contexts, you always need to use perform or performAndWait. Except for one situation: If you're using a main queue managed object context and you are running on the main queue, you can skip those methods.

In your examples:

  • Example 1 works because you used performAndWait. It's a private queue (not main queue) context, and you used it correctly.
  • Example 2 works because it's a main queue context and you used DispatchQueue.main.async. This is the one exception I mentioned above, so you're OK where.
  • Example 3 doesn't work because you're using a private queue context but you didn't use perform or performAndWait. You need to use one of those here to avoid crashing. You're on the same thread as example 1, but that doesn't matter. What's important is whether your Core Data code runs on the context's queue.

Upvotes: 1

kerry
kerry

Reputation: 2562

The understanding of how NSManagedObjectContext is created and which thread it is associated with is incorrect. When a NSManagedObjectContext is created on a particular thread using initWithConcurrencyType that does not mean that particular thread is NSManagedObjectContext's thread. It is just created on that thread and nothing else. Only when we call perform or performAndWait on NSManagedObjectContext, the block that we pass is run on the NSManagedObjectContext's thread. The results depict the same scenario.

However, if the NSManagedObjectContext is created with init only without concurrency type then current thread would be NSManagedObjectContext's thread.

Upvotes: 0

Related Questions