Objects with a database with bootable objects with backup access throw an NSObjectInaccessibleException

I use RestKit to capture objects from my RoR service and use CoreData to save some objects (more static types of table objects). TasteTag is one of those persistent objects:

#ifdef RESTKIT_GENERATE_SEED_DB NSString *seedDatabaseName = nil; NSString *databaseName = RKDefaultSeedDatabaseFileName; #else NSString *seedDatabaseName = RKDefaultSeedDatabaseFileName; NSString *databaseName = @"Model.sqlite"; #endif RKObjectManager* manager = [RKObjectManager objectManagerWithBaseURL:kServerURL]; manager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:databaseName usingSeedDatabaseName:seedDatabaseName managedObjectModel:nil delegate:self]; .. lots of fun object mapping .. RKManagedObjectMapping* tasteTagMapping = [RKManagedObjectMapping mappingForClass:[TasteTag class]]; [tasteTagMapping mapKeyPath:@"id" toAttribute:@"tasteTagID"]; [tasteTagMapping mapKeyPath:@"name" toAttribute:@"name"]; tasteTagMapping.primaryKeyAttribute = @"tasteTagID"; [[RKObjectManager sharedManager].mappingProvider setMapping:tasteTagMapping forKeyPath:@"taste_tags"]; [[RKObjectManager sharedManager].mappingProvider addObjectMapping:tasteTagMapping]; .. some more mapping .. 

I have data returned from a RoR server and they get object mapping as expected. The Core Data object also seems correct after RestKit returns the request:

 "<TasteTag: 0x6e87170> (entity: TasteTag; id: 0x6e85d60 <x-coredata://03E4A20A-21F2-4A2D-92B4-C4424893D559/TasteTag/p5> ; data: <fault>)" 

The problem is when I try to access properties of objects that may not be caused by an error. At first, I simply called properties that always returned as zero (although this should have led to an error):

 for (TasteTag *tag in self.vintage.tasteTags) { [tagNames addObject:tag.name]; //get error of trying to add nil to array } 

Having studied the errors during manual start ( http://www.mlsite.net/blog/?p=518 ), I tried to call [tag willAccessValueForKey:nil] , because of which:

 Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x6e7b060 <x-coredata://03E4A20A-21F2-4A2D-92B4-C4424893D559/TasteTag/p5>'' 

Searching for the object in .sqlite based on the key (TasteTag / p5) shows its comparison with the one I expect.

Other messages related to RestKit recommend disabling the object cache (which I do not use), as this is usually caused by deleting the object. But at this point I just read, not delete, and I don’t have a cache in place.

If I just call [TasteTag allObjects] , I can fully return all the objects and load them without problems. This is just the case when they mistakenly seem.

+6
source share
2 answers

Documenting my fix (reading: hacking) as suggested by Ryan.

It seems that the error is in how RestKit suggested that you will use the objects returned from their objectLoader:didLoadObjects: method. It seems like they believe that everyone will be supported using Core Data (and follow a stream similar to what Ryan talked about - let him synchronize with Core Data, then re-query) or that you will use all objects that are not supported by the underlying data , and just save these results.

In my case, I had a mix - the root array of non-Core Data-related objects, each of which contained an array of objects with Core Data support. Top-level objects are those for which I am not opposed to a server request, and I have no reason to stay locally outside the view in which they are shown. It seems that after objectLoader:didLoadObjects: the managed objects context is completed, the Core Data objects inside the objects parameter are deleted (assuming you will re-query for them), as a result of which future calls to entities will be considered as malfunctions, even if you cannot raise an error and load data ( NSObjectInaccessibleException results).

I went around it with an ugly hack - inside objectLoader:didLoadObjects: I access one of the context of the managed object of the Core Data object and copy it into the property in the view ( self.context = [tag managedObjectContext]; ). This prevents the release of context after the completion of objectLoader:didLoadObjects: allowing me to access objects without problems later in the view.

Another solution would be to manually re-query for each object using the new context and copy it back to the stored return objects. This can be done when you want to display them, or possibly do some post-processing in objectLoader:didLoadObjects: using the new context. The object identifier is still on the damaged object, so it can be used to re-query without problems even after the original RestKit context disappears. But it seems silly to repeat a query for every object in an object graph like this.

+4
source

I found a solution that worked for me (I'm not sure how applicable it is to your situation, but I am adding it as an answer, since it solved this (or very similar) problem for me):

A few days ago, I launched the RKTwitterCoreData example and noticed that it works fine, while mine, with very simple code at the moment and doing almost the same thing, didn’t. I have many failed errors. So I decided to modify all my RestKit related code to reflect how the RKTwitterCoreData example RKTwitterCoreData .

I am breaking it into pieces to try to help you follow my line of thinking at the time (since I do not think our problems are identical).

My assumption about initial implementation

Since RestKit can return objects to Core Data, I suggested that these managed objects can be used interchangeably. For example, I could use objects from Core Data in the same way as those that were retrieved from a remote web service. I could combine them together to get all the data.

I was wrong

I noticed that the RKTwitterCoreData code RKTwitterCoreData not go this way at least. A decent piece of my code matched them, but the biggest difference was that they did not consider these objects interchangeably. In fact, they never used objects that they received from remote data stores. Instead, they simply allow you to "fall through the cracks." I can only assume that they are added to the Core Data repository, as they work for them, and now for me.

More details

My application worked after changing my code to use this thread. I can only then assume that the unrealizable errors that we see are related to the use of Core Data-enabled objects that we return from the web service. If instead you simply ignore them and then fetch, you will get everything back (including the most recent request) and you should not get any unexecutable errors.

To develop, if you look at the RKTwitterViewController , you will notice that lines 45-61 handle loading objects:

 - (void)loadObjectsFromDataStore { [_statuses release]; NSFetchRequest* request = [RKTStatus fetchRequest]; NSSortDescriptor* descriptor = [NSSortDescriptor sortDescriptorWithKey:@"createdAt" ascending:NO]; [request setSortDescriptors:[NSArray arrayWithObject:descriptor]]; _statuses = [[RKTStatus objectsWithFetchRequest:request] retain]; } - (void)loadData { // Load the object model via RestKit RKObjectManager* objectManager = [RKObjectManager sharedManager]; [objectManager loadObjectsAtResourcePath:@"/status/user_timeline/RestKit" delegate:self block:^(RKObjectLoader* loader) { // Twitter returns statuses as a naked array in JSON, so we instruct the loader // to user the appropriate object mapping loader.objectMapping = [objectManager.mappingProvider objectMappingForClass:[RKTStatus class]]; }]; } 

Everything looks fine (at least compared to the way I did this download initially). But look at the delegate method objectLoader:didLoadObjects: ::

 - (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects { [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:@"LastUpdatedAt"]; [[NSUserDefaults standardUserDefaults] synchronize]; NSLog(@"Loaded statuses: %@", objects); [self loadObjectsFromDataStore]; [_tableView reloadData]; } 

The sample does not even touch the objects parameter! (Besides NSLog , of course ...)

Conclusion / TL; DR

Do not use managed objects that you return to objectLoader:didLoadObjects: as if they were fully supported by Core Data. Instead, ignore them and retrieve them from Core Data. All objects, including those received from the last request. Otherwise, you will get fatal errors (at least I did it).

+10
source

Source: https://habr.com/ru/post/905840/


All Articles