Coredata object fault in independent ManagedObject

2020-07-27 03:34发布

问题:

I have an operation object which creates it's own Managed Object Context. The operation object performs calculation basically to take off load from main thread while main thread concentrates on the UI aspect. This operation object's MOC shares a common persistent store across the application.

It sometimes happens such that, while the operation is being performed, the MOC of main thread is notified of changes in backend. So, I try to merge the changes of MOC, where some of the deletions in model objects might happen. In some rare cases, as I observe, the operation object might not have been realized yet and they are in fault state and at the same time main thread stores the changes into MOC (and to persistent store). My app is crashing in such scenario.

As per the standard texts, I have a dedicated MOC for my threads and as per my understanding, the data for faults should be accessed from the MOC of the thread. Does it make a difference in accessing fault requests from MOC of the thread, when say, the same object might have been deleted from the store?

Please see this stack trace:

What would be a better way of handling this?

I do understand from this post: https://stackoverflow.com/a/5722914/260665 that the managed object context of thread is not aware of the changes underlying the store and the faulted objects are expected to exist in the store. So, better it would be to either:

  1. Update the collection of objects in the thread when there are any underlying changes (deletion) in the store by using the NSManagedObjectContextDidSaveNotification notification (From: https://stackoverflow.com/a/5722914/260665)

  2. Check whether the NSManagedObject's record exists in the store before trying to access it's properties by using -existingObjectWithID on the MOC (From: https://stackoverflow.com/a/14297708/260665)

  3. Handle exceptions in code? (Last resort, from: https://stackoverflow.com/a/15361906/260665)

I cannot possibly go ahead with solutions 1 & 2 for it's not the NSManagedObject's properties directly am accessing, I have NSFetchedResultController which have sort descriptors inside which the app is crashing:

-(NSMutableArray*)fetchedTaskObjects
{
    if (nil==fetchedTaskObjects_ && self.taskLocalFetchedResultsController && self.persistantTaskFetchedResultsController)
    {
        NSArray *allNonPersistantTasks = [self.taskLocalFetchedResultsController fetchedObjects];
        NSArray *allPersistantTasks = [self.persistantTaskFetchedResultsController fetchedObjects];

        fetchedTaskObjects_ = [[NSMutableArray alloc] init];
        [fetchedTaskObjects_ addObjectsFromArray:allNonPersistantTasks];
        [fetchedTaskObjects_ addObjectsFromArray:allPersistantTasks];
        NSSortDescriptor *tasksSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"self.mockTaskCounter"
                                                                              ascending:YES];
        NSSortDescriptor *shortTextSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"self.shortText"
                                                                                  ascending:YES];
        NSSortDescriptor *headerTextSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"self.accountAssignmentText"
                                                                                   ascending:YES];
        [fetchedTaskObjects_ sortUsingDescriptors:[NSArray arrayWithObjects:tasksSortDescriptor, shortTextSortDescriptor, headerTextSortDescriptor, nil]];
    }
    return fetchedTaskObjects_;
}

So any suggestions of finding the solution in a right way?

Edit: The error (exception) which I am getting:

CoreData could not fulfill a fault for '0x2b0298a0 <x-coredata://E7E91AFC-5BE6-4996-B28F-92CD115A5D0A/CSTaskRegister/p14746>'

Which line it crashes?

It crashes in any one of the sort descriptors where the internal property of the faluted Managed object is tried to access by the sort descriptors for sorting purpose. Since it is a thread, we are not sure when exactly the underlying object is deleted in the store. So, the thread crash sometimes also occurs in my statistics calculation code.

Why am combining the result of two FRC?

Because, the project initially started out with different MOCs for these 2 cases, now though the MOC is single the differentiation still remains. But I don't think it is a problem to have discrete FRCs with it's own purpose.

回答1:

Did enough research, the only way to avoid this happening is to update the collection by listening to the notifications (1st solution mentioned in the question) and another approach is to double check with -existingObjectWithID (2nd solution in question) before accessing the properties.

But in my case, the properties of deleted (from persistent store) Managed Objects are being accessed internally by the sort descriptors. Leaves me no choice but to handle the exception (3rd solution in question) in the thread and neglect the (erroneous) results of this thread.



回答2:

If you cannot merge or don't want to touch the operation MOC - for example because it is currently in use, you need to guarantee that the operation and the data update become serialized.

The idea is, when the operation is running it assumes that its current view of the state of data is constant, and hence must not be changed.

You have a few choices to guarantee this:

If an operation is running, and you got an update-request AND want to immediately update the data including the shared persistent store, you need to first cancel the operation and wait until the operation has been finished (no accesses occur subsequently on the op's MOC). Then start updating the data in the main MOC. When the update has been finished including the persistent store, you may re-start the operation thereafter with a newly initialized or properly merged MOC - reflecting the actual state of the data.

Likewise, you may defer the update request until after a running operation has been completed.