Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debugging NSObjectInaccessibleException - The NSManagedObject with ID:0x123456789 has been invalidated

Tags:

core-data

I have a bug I'm struggling to track down. I believe what's happening is that I'm deleting an object from the underlying database whilst another managed object context (in another thread) has a fault on it and gets the 'NSObjectInaccessibleException' when it tries to fulfil the fault.

The scenario is that I have a view accessing the data through one context meanwhile in the background, another threat is purging out of date records from the store. The background thread should only be purging objects which are not required by the view - this obviously isn't the case but I'm having trouble tracking down exactly what happens. By the time I see the defect, it's too late and it is a relatively rare defect that mainly only happens in the field.

Hence my question: Are there any tricks I'm missing when debugging CoreData - can I track lifetimes of objects from one context in another? I.e. when I delete my object is there an easy way to see if any other contexts have a reference to that same object? Using that, I could build some test code to check my logic and find the error.

like image 846
Rog Avatar asked Aug 09 '09 07:08

Rog


3 Answers

I encountered this error earlier and the culprit was that I was cleaning up (completed released) my context and then later tried to access an object (previously) managed by that context.

In my case, the context was a "scratch" context that goes away when the view was closed. However, I had a background job that the view had spawned that wanted to update the object.

I ended up making an accessor for the managed object that returned nil when [managedObject isFault] was true. Then in my code I was checking the value of that accessor selector to make sure I had a valid object to work with (say when my background job finally finished its work).

I'm pretty new to Core Data, so there's probably a better/smarter way to do this, but I think it fixed the issue for me.

like image 186
Jason Avatar answered Dec 04 '22 01:12

Jason


I just faced this issue me too. I did some refactoring to follow Apple's "find-or-create" pattern for a bulk import of data. I created a new context dedicated to the import, by setting the undomanager to nil as suggested. So:

// create a new special context for the bulk import of data
NSManagedObjectContext *importContext = [[NSManagedObjectContext alloc] init];
[importContext setPersistentStoreCoordinator:_persistentStoreCoordinator];

// avoid tracking for undo/redo operations
[importContext setUndoManager:nil];

then I created a fetchRequest to retrieve ids of objects stored in the database, and inside the import loop I tested object's id to find if it was contained inside the array of ids retrieved... the problem was that at a given interval I was saving the importContext and resetting it. And since I did erroneously referring to importContext rather than defaultContext I get that error. I fixed simply by changing:

NSArray *storedObjects = [importContext executeFetchRequest:checkRequest error:&fetchError];

with:

NSArray *storedObjects = [defaultContext executeFetchRequest:checkRequest error:&fetchError];
like image 42
daveoncode Avatar answered Dec 04 '22 00:12

daveoncode


What is the second context doing when it tries to fault in the object which has been deleted from the persistent store?

This sounds like a bug which may have 2 parts: you aren't merging changes from your peer context, and you have a logic bug which is causing you to use an object in thread B which has been deleted in thread A.

Typically you'll want to merge changes from a peer context using -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:].

like image 39
Jim Correia Avatar answered Dec 04 '22 02:12

Jim Correia