NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault - ios

My iOS app uses core data via multiple threads. I am getting some crash reports with the following message: "'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x1e07a9b0 ''
I understand what is causing this problem - that the object was deleted but another thread is trying to access it. I am working to solve the problem but I want to add a check in the background thread to see if the object will fault in this manner.
My code at the moment relates to myObject.myValue. Is it possible to do some check, such as:
if (!myObject.myValue) {
return;
}
... so that it will get out of the method before doing anything that could cause such a crash? Or will simply calling myObject.myValue, even to see if it's null, cause such an exception to be thrown?

You could try and use existingObjectWithID:error::
Returns the object for the specified ID.
- (NSManagedObject *)existingObjectWithID:(NSManagedObjectID *)objectID error:(NSError **)error
Discussion
If there is a managed object with the given ID already registered in the context, that object is returned directly; otherwise the corresponding object is faulted into the context.
This method might perform I/O if the data is uncached.
Unlike objectWithID:, this method never returns a fault.
You could dO:
if ([myMOC existingObjectWithID:myObject.objectID error:&error])
...

You should verify that the object exists before accessing it's variables if you're having issues where the object may be deleted on another thread.
Two methods:
Refresh the view datasources whenever your data is being deleted. You can do this by registering for the NSManagedObjectContextObjectsDidChangeNotification notification and then parsing the userInfo on that notification to see which object was deleted.
Use code similar to below when you're passing data around to multiple threads.
Example:
// Cache and pass the object's ID off to another thread to do work on
// You can just store it as a property on the class
#try {
NSManagedObject *theObject = [managedObjectContext objectWithID:self.theObjectID];
// do stuff with object
}
#catch (NSException * e) {
// An entity with that object ID could not be found (maybe they were deleted)
NSLog(#"Error finding object: %#: %#", [e name], [e reason]);
}

You can check the NSManagedContext is existed when you use the NSManagedObject.
like this:
if (obj.managedObjectContext)
{
//do things
}

You can check [myObject isFault] where myObject is a NSManagedObject instance

You could give a try to use :
shouldDeleteInaccessibleFaults
property on managed object context. As this article says it should change the behaviour of faulting already deleted object.
https://cocoacasts.com/what-are-core-data-query-generations/
Edit:
Since iOS 9 (when it was added) this property default value is YES.

Related

Illegal attempt to establish a relationship 'order' between objects in different contexts

The orderDetail is related with consume. The consume was fetched from CoreDataContext as a parameter passed to here and was exist.
orderDetail = [NSEntityDescription insertNewObjectForEntityForName:#"OrderDetail" inManagedObjectContext:CoreDataContext];
orderDetail.order = consume
But this code throws an exception:
Illegal attempt to establish a relationship 'order' between objects in different contexts
I checking the exception with NSLog
NSLog(#"%#", orderDetail.managedObjectContext);
NSLog(#"%#", consume.managedObjectContext);
Then I found that orderDetail.managedObjectContext exists, but the consume.managedObjectContext is nil
Is this why the exception threw? I don't know why this happened.
Update my question,more info:
First
I have only one Context
Second
this is happened when I delete the consume-entity and reinsert consume-entity:
When I push A viewController to B viewController(B viewWillAppear) I request the orderDetail and established the relation of consume,when I pop to A in method viewWillAppear I request consume ,when I received data I called
[ CoreDataContext performBlock:^{
[Consume cleanTheContext:CoreDataContext];...
... ... ... ...
for (NSDictionary *consume in consumes) {
[Consume consumeWithLecaiInfo:consume inManagedObjectContext:CoreDataContext byUser:user];
}];
to clean the old consume and insert new consume (I know I should update consume ,rather than delete and reinsert,but I don't know why consume lost it's managedObjectContext in this way)
In reality this error occur once in a while , push A,pop B, push A, pop B ... exception threw out .
Error is self explanatory, you can modify you code to retrieve consume object from the same context where you create orderDetail:
OrderDetail *orderDetail = [NSEntityDescription insertNewObjectForEntityForName:#"OrderDetail" inManagedObjectContext:CoreDataContext];
orderDetail.order = [CoreDataContext objectWithID:consume.objectID];
objectID is a proper way to transfer objects from one NSManagedObjectContext to another
For a relationship to be formed both objects must be created from the same NSManagedObjectContext.
Normally this is not an issue because your UI should be using a single context and your creation of objects should be using NSEntityDescription.insert....
Since you have a nil context in one of your objects, that means you are creating it either with a nil context somewhere or you are intentionally passing a nil into NSManagedObjectContext.init.... Either situation is a problem.
Trace your creation code and put breakpoints in. Find where the nil is and fix it.

Saving NSManagedObjectContext on Stackmob -- uploads to server, but crashes app

I have been running into an issue in using StackMob as the backend of my iOS application (though I'm not sure if this is an issue in wrongly using StackMob's methods or an iOS issue).
I am allowing a user to create a post object that is just a subclassed NSManagedObject, and uploading that to the server to be used in other parts of the application. The issue that arises occurs in the method:
[NSManagedObjectContext saveOnSuccess:<^(void)successBlock> onFailure:<^(NSError *error)failureBlock>];
Here, I am using a StackMob method for asynchronously saving the MOC found in the NSManagedObjectContext(Concurrency) Category Reference.
The view before this one performs a fetch on recent posts, and in the case where the fetch is not performed posting works fine, but if a fetch was performed then in saving the MOC in order to upload the new post I receive the following output as an error message:
2013-09-11 17:08:09.284 imageTagging[1824:1843] -[__NSDictionaryI bytes]: unrecognized
selector sent to instance 0x1e3123d0
2013-09-11 17:08:09.291 imageTagging[1824:1843] *** Terminating app due to uncaught
exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryI bytes]: unrecognized
selector sent to instance 0x1e3123d0'
*** First throw call stack:
(0x318cb3e7 0x395c6963 0x318cef31 0x318cd64d 0x31825208 0x321631cf 0x3216b991 0x15ea99
0x318c8757 0x15e109 0x15dabf 0x10d1c3 0x318d05b7 0x10cd4d 0x10c829 0x10923b 0x1076d9
0x3166c431 0x316c44d1 0x1685c3 0x316c7e5d 0x399e3b3b 0x399e167d 0x399e4613 0x399e47d9
0x39a087f1 0x39a08684)
libc++abi.dylib: terminate called throwing an exception
(lldb)
The data is still uploading to the StackMob server, and can be called upon when the app is run later -- but the app crashes in trying to save it. All of this is performed in the view controller. I've tried to enforce all MOC saves to be performed on the main thread, but the error still occurs. I've also tried dispatching a "save queue" and updating the UI after save completes. This method seemed to work for a bit, but then the errors came up again (may have just been a fluke). I also tried to do this with the synchronous save calls in the documentation
The same error occurs when trying to perform other saves as well (such as after creating a new user or when updating a user's information), and all come down to the same function call causing the problems. It may also be worthwhile to note that the error is always the same (specifically that a type __NSDictionaryI is trying to access its unrecognized selector bytes.
Here is the full method call with the input parameters filled out:
//save context
[[[[SMClient defaultClient] coreDataStore] contextForCurrentThread] saveOnSuccess:^{
NSLog(#"You created a new Post object!");
[[[[SMClient defaultClient] coreDataStore] contextForCurrentThread] refreshObject:newPost mergeChanges:YES];
NSLog(#"refreshed");
} onFailure:^(NSError *error) {
NSLog(#"There was an error! %#", error);
}];
UPDATE: I have narrowed down the problem to a mishandling of information returned from a fetch performed by the previous view controller. Specifically, it occurs after the results are fetched in trying to use the data to update.
As a result of this new insight, the question I am really facing is how to properly save a managed object in the context after a fetch. I believe StackMob takes care of creating the managed objects after the fetch (i.e. server query). I've tried creating a new object from the results array (each "obj" is an NSManagedObject) with:
[results enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSManagedObject *newObj = obj;
}];
I've also tried referencing the fetched results by object id (each "obj" is an objectID) with:
[results enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSManagedObject *newObj = [self.managedObjectContext objectWithID:obj];
}];
Any insight on how to properly do this would be greatly appreciated!
UPDATE 2: It looks like the error is actually occurring in trying to use and save geolocation data. In order to use the queried objects' geolocation data, it must be unarchived -- but to save it, it must be archived. I'm looking into how to do this now, and if I come across a good solution, I'll update again.
FINAL UPDATE: Got it figured out! It turns out that the issue I was having was that I was unarchiving the geolocation data to update the UI and do some calculations, and while I was archiving it again to be stored properly, I created an annotation on a map that referenced the unarchived data. As a result, the MOC maintained the data that could not be saved via the StackMob methods. By only saving the archived data, I can save as often as I'd like and just unarchive the geodata when it needs to be used. Problem solved!
Please feel free to comment if anyone comes across a similar problem and needs some insight or references!
I am just going to put my final update in here as an answer, since it explains how I solved the issue.
FINAL UPDATE: Got it figured out! It turns out that the issue I was having was that I was unarchiving the geolocation data to update the UI and do some calculations, and while I was archiving it again to be stored properly, I created an annotation on a map that referenced the unarchived data. As a result, the MOC maintained the data that could not be saved via the StackMob methods. By only saving the archived data, I can save as often as I'd like and just unarchive the geodata when it needs to be used. Problem solved!
Please feel free to comment if anyone comes across a similar problem and needs some insight or references!
Moral of the story, if you are running into issues similar to this, make sure you are not (even if you don't mean to be) storing references to the unarchived SMGeoPoint data in any of your managed objects. It is trying to store those that causes the problem.

Merging Changes between NSManagedObjectContexts (Multithreaded)

I have a problem/crash merging the data of different NSManagedObjectContexts (iOS 6.1, Xcode 4.6).
Most of the time the error that rises is the following:
CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. statement is still active with userInfo (null)
One time i got this error:
An observer of NSManagedObjectContextDidSaveNotification illegally threw an exception. Objects saved = { $OBJLIST } and exception = statement is still active with userInfo = (null)
Sadly there is no value in the stacktraces i got. They just show symbols that are CoreData internal (if any).
Our CoreData stack:
1 NSPersistentStoreLocator shared by all threads
1 unique MOC per thread ( created on first need )
All MOCs are saved in a Dictionary
An observer is added for the notification NSManagedObjectContextDidSaveNotification to update the MOCs when one is saving to the store. The defined selector is calling mergeChangesFromContextDidSaveNotification on every other thread/context except the one that did the save operation.
+ (void)mergeChanges:(NSNotification *)notification {
NSManagedObjectContext *ctx;
for ( NSNumber *threadId in [__managedObjectContexts keyEnumerator] ) {
ctx = [__managedObjectContexts objectForKey:threadId];
if ( notification.object != ctx ) {
[ctx mergeChangesFromContextDidSaveNotification:notification];
}
}
}
Steps to produce the error:
In a background thread CoreData data objects that arent needed anymore (unreferenced by other objects) are being deleted.
[[CDUtils managedObjectContext] deleteObject:obj];
[[CDUtils managedObjectContext] save:&error];
While this is happening the user can navigate throughout the application. Userinteraction (i.e. opening a tableview) can trigger executeFetch calls on the moc of the main thread.
Every thread uses the same NSPersistentStoreLocator but a different/unique MOC.
We tried different methods of locking with NSLocks and the lock on the NSPersistentStoreLocator for threadsafety. I.e. enclosing the mergeChanges Method and the save operation each by a lock/unlock or enclosing both methods in the same lock/unlock. Sadly we had no succes thus far.
[__storeCoordinator lock];
[__storeCoordinator unlock];
I'd be thankful for every piece of advice you can give me to approach a solution. Thank you for your time!
For the people interested. I managed to make things work with multiple threads / MOCs. I basicly solved the original problem / those errors i had by locking PSC and MOCs correctly. The next problem that arose was how to know if its save to mergeChanges on a context. I cant lock nor should mergeChanges on a MOC that has no running thread anymore. But how do i know if the thread is running or not? If i just check for NSThreads "isExecuting"-Method it might happen that the thread exits just after i checked the BOOL. Im trying an easier approach now where i just merge into the main thread.

ManagedObject has nil context after save

This is on a background thread. It's a private concurrency type context and it's performed within context performBlock. I'm doing it right, at least I think I am.
I'm not a beginner to core data, however I could be missing something obvious - as I've been staring at this a while.
Here is the code:
FFXCollection *backgroundCollection = (FFXCollection *) [context objectWithID:collectionID];
//At this point backgroundCollection.managedObjectContext is the same as context
NSError *error = nil;
[context save:&error];
NSLog(#"error %#", error); //Note that there is no error here
if (!backgroundCollection.managedObjectContext) {
DLog(#"why not?"); //At this point the managedObjectContext is nil!!!
}
Why is the managedObjectContext becoming nil inside the managed object?
The example is contrived, but demonstrates my problem. In my actually code, a save occurs and then i'm trying to set up a relationship. Then when another save occurs I get a validation error because of the above.
Thanks
Use existingObjectWithID instead.
existingObjectWithID:error: Returns the object for the specified ID.
(NSManagedObject *)existingObjectWithID:(NSManagedObjectID )objectID error:(NSError *)error Parameters objectID The object ID for the requested object. error If there is a problem in retrieving
the object specified by objectID, upon return contains an error that
describes the problem. Return Value The object specified by objectID.
If the object cannot be fetched, or does not exist, or cannot be
faulted, it returns nil.
Discussion
If there is a managed object with the given ID already registered in the context, that object is returned directly; otherwise the
corresponding object is faulted into the context.
This method might perform I/O if the data is uncached.
Unlike objectWithID:, this method never returns a fault.
Availability Available in OS X v10.6 and later. See Also –
objectWithID: – objectRegisteredForID: Declared In
NSManagedObjectContext.h

SIGABRT in NSFetchedResultsController delegate methods after mergeChangesFromContextDidSaveNotification

The good facts:
I download data from the server, and, via Core Data thread confinement, save the data, and when the background MOC is saved, the main MOC gets merged.
All the saving operations go ok
Also the merging of the MOC happens without any problems
The bug I'm hunting:
When my UITableView with NSFetchedResultsController is active (i.e. on the screen), and the saving is happening, the app crashes with a SIGABRT that takes me to the mergeChangesFromContextDidSaveNotification line in AppDelegate.
What is the most strange part is, that when the delegate of the FRC is nil, or when it is my view controller but i don't implement any FRC delegate methods, the crash doesn't happen and I don't have any problem. But when I implement any of the delegate methods (even empty, without a single line of code), the app crashes with the same bug. It means that the methods are not even being fired, the problem is not in the code inside these methods.
The strangest part 2 (CHECK UPDATE 2 BELOW): the crash happens with a [__NSCFNumber length]: unrecognized selector sent to instance and I don't call any 'length' property in my CoreDataManager neither in my AppDelegate class
The witness: console
<CoreDataManager.m:(338)> Saved data from server
<AppDelegate.m:(352)> Will merge
<CoreDataManager.m:(338)> Saved data from server
<AppDelegate.m:(355)> Did merge
<CoreDataManager.m:(338)> Saved data from server
<AppDelegate.m:(352)> Will merge
<AppDelegate.m:(355)> Did merge
<CoreDataManager.m:(338)> Saved data from server
<CoreDataManager.m:(338)> Saved data from server
<CoreDataManager.m:(338)> Saved data from server
<MyTableViewController.m:(134)> Fetched results controller did fetch
<CoreDataManager.m:(338)> Saved data from server
<CoreDataManager.m:(338)> Saved data from server
<CoreDataManager.m:(338)> Saved data from server
<AppDelegate.m:(352)> Will merge
<CoreDataManager.m:(338)> Saved data from server
[__NSCFNumber length]: unrecognized selector sent to instance 0x13318050
Some code - Merging the MOCs
- (void)managedObjectContextDidSave:(NSNotification *)notification
{
NSManagedObjectContext *sender = (NSManagedObjectContext *)[notification object];
if ((sender != self.managedObjectContext) &&
(sender.persistentStoreCoordinator == self.managedObjectContext.persistentStoreCoordinator))
{
dispatch_async(dispatch_get_main_queue(), ^{
DebugLog(#"Will merge");
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
DebugLog(#"Did merge");
});
}
}
Update 1
Following Cocoanetics hint, I created a NSNumber category to check who is calling length. I got what you see below, and a crash in [__NSCFNumber _fastCStringContents:]: unrecognized selector sent to instance.
Update 2
Enabling zombies didn't help =(
Make sure that you are only observing the notification from other MOCs. If you save there this triggers another such notification and you might be going in an endless loop that fails after one or two iterations because an object had been released by ARC.
Yours sounds like a memory issue. Check your ARC ownership qualifiers and enable NSZombies. Enabling NSZombies will help you narrow down the object that was released prematurely.
When you enable zombies you will see a "message sent to a deallocated instance" instead. Check which object was released prematurely and update your question.
Well, after months and hours, I finally got a solution. It works, and I would love to hear some opinions on why.
So, as I said, the saving was working 100%, as well as the merging notifications. If I set the NSFetchedResultsController delegate to nil, there was no problem. However, setting the delegate to my UIViewController, made the app crash.
I thought it could be, maybe due to my code when the delegate methods were triggered. But the app crashed even before that. So I followed Cocoanetics tip, to create a category and try to figure out who was calling the length method to the NSNumber object. After that, I saw that the NSPredicate was calling - (BOOL)evaluateWithObject:(id)object; before getting to the crash. In the same way, I did a category to override it:
#interface NSPredicate (PractiPredicate)
- (BOOL)evaluateWithObject:(id)object;
#end
#implementation NSPredicate (PractiPredicate)
- (BOOL)evaluateWithObject:(id)object
{
NSLog(#"Evaluate was called. Object class %#", NSStringFromClass([object class]));
MyManagedObject *myManagedObject = object;
NSLog(#"Is fault? %d", myManagedObject.isFault);
NSLog(#"myManagedObject changed and already have propertyA? %d", myManagedObject.propertyA != nil);
return YES;
}
#end
So, for my surprise, it worked, and generated te following logs:
Evaluate was called. Object class MyManagedObject
Is fault? 0
myManagedObject changed and already have propertyA? 1
I decided to print "Is fault?" because I thought that this mess was related to NSManagedObject faulting, but, for what it printed, it's not.
Question for the comments: What do you think that could have generated this problem here?
Most likely the problem does not lie in the code you posted but in how you deal with the changes in the fetched results controller delegate. Those are just being triggered by the merge.
I had the same problem and in my case, i found out that the reason for the crash was an incorrect predicate. I had a predicate like this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"attribute > 0"];
Where attribute was a string. I corrected it as:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"attribute.length > 0"];
Now, my code is running fine. Make sure you check all the predicates in your code since this can also be a reason for this crash.

Resources