Coredata - "NSObjectInaccessibleException - CoreData could not fulfill a fault"
Asked Answered
D

4

32

I am new to Core data and still figuring out the nuts and bolts and this error has been bugging me for hours and I cant seem to find a solution. Any help is greatly appreciated.

The problem is like this

I have two views which fetch data from the server and Update the UI. I have set up the flow this way

view1 -> Send HTTP Req from Server - Receive Callback -> Save Data To Coredata -> Read From Core Data and display on the UI (callback and saving/reading Coredata happen in ViewController)

view2 -> Send HTTP Req from Server - Receive Callback -> Save Data To Coredata -> Read From Core Data and display on the UI (callback and saving/reading Coredata happen in ViewController)

View 2 repeats this process every 3 seconds as this is a auto refresh screen.

The problem is whenever I try to switch between views 1 and 2 real fast, it crashes the app with above error. If I wait for a few seconds on each view (wait for data to be fetched from the server), it works fine. Am I doing something wrong, what do I need to modify?

- (void) refreshData {
    [super refreshData];
    [[UserDataFactory sharedSingleton] refreshLoggedInUserDataAndRespondTo:self user:self.user];
}

- (BOOL) refreshDataCallback:(QExtendedHTTPOperation*)responseOperation {
    [self saveToCoreData: responseOperation.responseArray];
    NSMutableArray *tmp = [[NSMutableArray alloc] initWithArray:[self readFromCoreData]];
    [self setData: tmp];
    [tmp release];
    [self.tableView reloadData];
    return YES;
}

- (void) saveToCoreData:(NSArray *) responseArray{
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"CoreView1" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];
    [fetchRequest setReturnsObjectsAsFaults:NO];
    NSError *error;
    NSArray *items = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
    [fetchRequest release];

    for (NSManagedObject *managedObject in items) {
            [self.managedObjectContext deleteObject:managedObject];
    }

    for (int i=0; i<[responseArray count]; i++) {
            CoreView1 *coreView1_ = [NSEntityDescription insertNewObjectForEntityForName:@"CoreView1" inManagedObjectContext:self.managedObjectContext];
            coreView_.id = [[responseArray objectAtIndex:i] id];    
            [self.managedObjectContext insertObject:coreView1_];
    }
    [self saveContext:self.managedObjectContext];
}

- (NSArray *) readFromCoreData{ 
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"CoreView1" inManagedObjectContext:self.managedObjectContext];
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    [fetchRequest setEntity:entity];
    [fetchRequest setReturnsObjectsAsFaults:NO];
    NSError *error;
    NSMutableArray *fetchedObjects = [[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
    [fetchRequest release];
    return [fetchedObjects autorelease];
}

This is the sample code I'm using, even View2 has the same callbacks and follows the same flow.

Edit 1 Forgot to mention this earlier, I always get the error in saveToCoreData method. Also, one more thing to note is that If I remove the code to delete objects it all works fine (I need to remove all the existing data from the table before I save the updated data). Not sure whats happening though.

Dulcia answered 8/4, 2011 at 6:30 Comment(0)
K
42

The reason removing your delete code makes it work is because you are removing the data in the persistent store without updating the other view that still has managed object instances tied to that data still in memory. Remember, while Core Data deals with objects, each object has to have a row in the database behind it. When you delete that row, Core Data gets angry.

So, to fix this problem, and still delete your data, you should have your views listen for NSManagedObjectContextWillSaveNotification and/or NSManagedObjectContextDidSaveNotification notifications and update your views with the most up to date versions of data in your store. It is at this point you should throw away any Core Data objects your views are holding onto, and reload them from the store.

Kryska answered 19/4, 2011 at 21:26 Comment(2)
casademora, thanks for the detailed response. Will implement the notifications. Thanks very much again.Dulcia
Thanks Casademora, I'm now listening to the notifications and there appears to be no crashes. Appreciate your help.Dulcia
W
3

I just resolved this error in my code. Seems my cache was corrupted in some way. I used Christopher Pickslay & Keil Gillard's suggestion of deleting or renaming my cache, and voila, crash resolved. NSFetchedResultsController index beyond bounds

Winkelman answered 4/1, 2013 at 0:52 Comment(1)
This solution did it for me! My cache was named "Master". After changing it to another name it worked like charm. Thank you. Quite easy and much easier than a lot of event handlingWestlund
I
0

For info, yesterday, I had the same error. I checked in a live version of the app, and it was still there. yikes.

I ran through all the permutations of adding info to the core data stack until the crash occurred.

I then looked at the sqlite file from the app in the simulator library using SqliteManager. I found a data error in one table. This had occurred as there is a starter db used the first time the app was run. The error was in the starter db.

Ran the app again, testing each field in each table. Found several more occurrences. I remember reading this question, and thought maybe someone else could benefit from my mistake.

Insessorial answered 29/9, 2011 at 11:54 Comment(0)
D
-1

Your over releasing fetchedObjects in your readFromCoreData method. executeFetchRequest will return to you autoreleased objects anyway. When the current run loop finished executing (when you're jumping from views) then it attempt to release the objects twice (assuming you haven't used your own autorelease pools).

I'd change

return [fetchedObjects autorelease]; 

to

return fetchedObjects;
Drowse answered 8/4, 2011 at 6:42 Comment(7)
I just re-read your question and maybe my suggestion (which is a problem for you) isn't the full answer to your specific error. I missed that you were getting NSObjectInaccessibleException and assumed you were getting a EXC_BAD_ACCESS.Drowse
Damien, thanks for the quick response. You are right, I shouldn't have auto released the fetchedObjects. I corrected it. I forgot to mention this earlier, it always fails in the saveToCOreData method. If I remove the saving option the app runs fine.Dulcia
@Naz Hussain: Is everything being done from the same thread? Especially the callbacks you are getting from your HTTP code?Drowse
No, each HTTP req is a different thread and I'm using different managedObjectContexts for each thread.Dulcia
Are you ever passing nsmanagedobjects between threads? If so are you using object id's? In particular where you are interacting with UIKit on main thread?Drowse
Thanks Damien for pointing me to the right direction (based on what you said I created diff contexts for each thread). Appreciate your response.Dulcia
No problem Naz, glad I was helpful.Drowse

© 2022 - 2024 — McMap. All rights reserved.