Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'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.

like image 520
Tim Avatar asked Aug 21 '12 07:08

Tim


2 Answers

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!

like image 161
Tim Avatar answered Sep 28 '22 07:09

Tim


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];
        });
    }
}
like image 31
Stanislav Pankevich Avatar answered Sep 28 '22 07:09

Stanislav Pankevich