Tim
Tim

Reputation: 597

'Object's persistent store is not reachable...' on main thread

I'm one of the many getting the following Core Data error (133000):

Object's persistent store is not reachable from this NSManagedObjectContext's coordinator

As I've seen all over SO, this is because of Core Data's threading restrictions.
However, I've complied to all the correct threading restrictions, and yet this error is still coming through.

I create an NSManagedObject using the below code to perform the function on the main thread:

NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
                                                                        selector:@selector(commitPlayer:)
                                                                          object:message];
[[NSOperationQueue mainQueue] addOperation:operation];
[operation release];

This function then creates the object and it's perfectly happy.
Then attempting to fetch the object with [objectContext existingObjectWithID:objectID error:&error] returns nil with the aforementioned error.

I've also wrapped the object fetching with an @synchonized mutex lock block just in case, and yet it is still failing to fetch that object.

It appears that saving the context before hand fixes this, however I want to fetch the object regardless as to whether it's saved or not as the DB shouldn't be saved at this point.

Upvotes: 12

Views: 12118

Answers (5)

ethoooo
ethoooo

Reputation: 555

I had the same error & it turns out it was because the temporary child MOC I was using to get object with ID did not have it's parent set to my main MOC.

All I ended up needing to do was this.

let tmpContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
tmpContext.parent = CoreDataStack.context

And then get objects with IDs if that's your goal.

Good luck :)

Upvotes: 1

Stanislav Pankevich
Stanislav Pankevich

Reputation: 11368

I want to share my case, which was related to NSManagedObjectContextDidSaveNotification:

I had this problem only in my UnitTests target, in the test cases which involved interaction with Core Data stack.

In my configuration of unit tests target each such case is preceded by beforeEach block having cleanUnitTestsDatabase() macros which performs a removal of Core Data's .sqlite3 file and reinstantiatiates a managed object context and persistent store coordinator to a new fresh state, so I have a whole database in a clean state each time I run in a new/next test case.

This caused the problem, because sometimes the following method was called after the current database had been axed because the current test case has been already passed and the next following test case has been entered already:

- (void)managedObjectContextDidSaveNotification:(NSNotification *)notification {
    NSManagedObjectContext *savedContext = [notification object];

    if ([savedContext isEqual:self.mainQueueManagedObjectContext] == NO) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.mainQueueManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
        });
    }
}

Upvotes: 5

hokkuk
hokkuk

Reputation: 675

I wanted to show an example of this problem of two reads of the NSManagedObject that will crash into each other and produce the error message in the Original question. but this will happen only if you have the NSManagedObjectContextDidSaveNotification set up, and are doing a mergeChangesFromContextDidSaveNotification... interestingly this notification was firing on a read of a valueForKey... not sure if there is a problem there.

    NSArray *theEvents = nil;
NSError *error = nil;

if ([currentObject valueForKey:@"event58Identifier"]) theEvents = [eventStore calendarItemsWithExternalIdentifier:[currentObject valueForKey:@"event58Identifier"]];

and how i solved it :0)

    NSArray *theEvents = nil;
NSError *error = nil;
NSString * identEvent = [currentObject valueForKey:@"event56Identifier"];

if (identEvent) theEvents = [eventStore calendarItemsWithExternalIdentifier:identEvent];

in any case, put in an NSLog and find as many saves as possible, because more than likely you don't need so many saves or reads, and try to get it all into one read or one write.

Upvotes: 2

HoBa
HoBa

Reputation: 3604

I was getting the same error, though my problem was slightly different. I had multiple NSManagedObjectContexts with different NSPersistentStoreCoordinators (a 3rd party library). They were having conflicts when the NSManagedObjectContextDidSaveNotification notification happens. That is, you're notifying one of the NSManagedObjectContexts of changes it doesn't know about. That's what "Object's persistent store is not reachable from this NSManagedObjectContext's coordinator" was saying with different words :)

Upvotes: 1

Tim
Tim

Reputation: 597

Found the answer.
The problem was nothing to do with threading. It was simply because there was already a write happening (Hence why people assume via Occam's Razor that it's a threading issue).

The real reason this error shows, is not to do with threading, like the internet says, but because a read/write is already in progress at the same time you're attempting another read/write.

[objectContext existingObjectWithID:objectID error:&error] performs I/O on the database in order to guarantee the object that you get back actually does exist. However, in my case, I was already performing a read higher up in the stack.

It was attempting to read the parent's children, then attempting to read each child. Where, in fact, I should be using [objectContext objectWithID:objectID] to fetch a faulted object, and then perform the appropriate I/O once it's required.

This is not only the correct way of doing things, it also saves memory by not loading fifty billion child entities when you only wanted the list of them.

Hope that helps clarify things for someone!

Upvotes: 15

Related Questions