How to make NSManagedObject not fault? - ios

I'm currently debugging a big project written by another developer. The project uses CoreData which i am very new to. I'm having a crash which happens due to the fact that some NSManagedObject is being a fault(i have poor understanding of what fault is) and i would like to convert the object into "not fault" and see if it helps. Reading documentation made me think that to make object not fault is equal to to fire fault (again i have poor understanding of what is "to fire"), so i decided to fire it by calling any method on it, and this method is hasChanges since it's not in the list of methods which don't fire fault. However even after calling this method the object still remained being fault. Can anyone give me an example of how to convert NSManagedObject into "not fault" state?

The exception you mentioned in a comment is:
Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x1f0627a0 <x-coredata://E40418A0-A8E5-4340-865F-A9DA2E0095DD/CoreObject/p288>''
The "could not fulfill a fault" message indicates that this is not simply a problem with firing the fault. In fact there are no special steps to fire a fault-- you just access the attributes, and if necessary, the fault fires automatically.
What this error is telling you is that you're doing something that causes a fault to fire, but that Core Data can't find any record of the instance you're using. This can happen in certain scenarios, for example:
Fetch an object, and leave it as a fault (i.e. don't access any attributes)
Delete it from Core Data, but keep a reference to the object (maybe in an instance variable)
Save changes
Try to access an attribute on the object you fetched in step 1.
At this point Core Data would normally use the object to look up the attribute value. But you already deleted it, so the attribute data is gone. Core Data throws this exception.
There are some other scenarios that can trigger this-- calling reset on a managed object context while keeping previously fetched objects around, or removing the persistent store (so that the data still exists in the store file, but the file isn't loaded anymore). In general it means Core Data is trying to look up data on an object that's no longer valid.
What you need to do:
Figure out which object is causing this. Setting an exception breakpoint, so that the debugger loads just as the crash is happening, is probably a good idea.
Get rid of that object. Preventing the fault from firing might prevent this specific crash, but as long as you still have this object, it's like a land mine in your app. It will explode and crash the app as soon as you touch it.
Figure out why you have invalid managed objects lurking in memory. Maybe you should have gotten rid of them earlier? Maybe you're accidentally deleting something you don't want to delete? For some reason you're keeping managed objects around when they're no longer valid. This is the core problem that's causing your trouble.

Faulting is the process whereby an object and/or its properties are fetched from the database on-demand.
For example, if you attempt to access a property or relation on some object, person.name, that object may not have its data in-memory, and it has to be fetched from the underlying data store. This is a fault.
Faulting is a normal part of how Core Data works, and should not be related to crashes. An object will be unfaulted when you try to access its properties and relations.

Related

Objects should not be both modified and additional

Part of a project I'm working in involves managing storages for products.
A Storage object contains StorageShelf objects, which define the amount of shelves. A StorageShelf, in turn, contains some other objects such as StorageLocation objects that determine the amount of products that can fit on a shelf. All These objects exist in CoreData. I also store Storages and StorageShelfs in a CloudKit database for synchronization purposes.
If I am running my app on two iPads, and add a storage with some shelves on one of the devices, it receives the notifications on the other and starts processing the updates it received.
I have a CloudOperationQueue in my app that has a maxConcurrentOperationCount of 1, in order to make sure only one update is made at the same time (This is because all objects of my app, also those out of the scope of this question are highly interdependent).
Inconsitently, this results in the following SIGABRT error message:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Objects should not be both modified and additional'
Specifically, this error occurs when I save the context after making modifications to one of the shelf objects I received. There is no consistency in which shelf.
Question
What does this error mean, and how can one debug it?
I recently got this error, also inconsistently. The issue was connected to adding an object and then saving the CoreData context on a background thread (part of a drag and drop update), while also trying to update a view showing a list of the objects on the main thread.
The fix is to cause things to happen synchronously, for me the easy fix was moving the save operation to the main thread, like (in Swift):
DispatchQueue.main.async {
// save your context
}
I was also getting this error recently, and also inconsistently. I had several core data objects with a 1:many relation and randomly when I would add an object to that relation and save context I would receive this exception.
For me the problem turned out to be I was using the NSPersistentContainer.viewcontext
in a background thread and
to write to core data
whehn according to these docs https://developer.apple.com/documentation/coredata/nspersistentcontainer viewcontext should be read only for the main queue. Switching to NSpersistentContainer.newBackgroundContext instead fixed the issue.

Definitive list of possible reasons NSManagedObjectContext save might fail

TL;DR version: is there a definitive list of possible reasons that -[NSManagedObjectContext save:] could fail? I'm looking in CoreDataErrors.h, but it's not clear which of the keys relate to NSDetailedErrorsKey.
Background: I'm maintaining a shipping app written in a combination of Swift and Objective-C. It uses JSQCoreDataKit. Someone else wrote the code originally.
We have a production crash that we have not been able to reproduce. We can tell from the stacktrace that it is crashing in an error handler following an NSManagedObjectContext save failure with EXC_BAD_ACCESS trying to log the NSError to the console. The save failure is on a child context and we know the model class which was being modified at the time.
Hence we can tell that the save is failing, but we have no detailed information about the reason for the save failure.
We want to work backwards from the reason for the save failure, in the hopes of working out a reproduction for the crash, in order to test any potential fixes.
The crash is actually a side effect of the save failure. So although we want to fix the crash, we also need to work out the reason for the save failure.
For 95% of users, the save happens without problems.
The model has some non-optional fields and the data comes from JSON parsed from a server response. At this stage we have no reason to suspect the server is sending us bad data.
Does anyone know of a definitive list of possible reasons for a save failure that we could work through, eliminating options?
So far, I'm aware of:
validation failure e.g. a missing a required value, or a value outside specified max/min values in the model.
a possible save conflict (see NSPersistentStoreSaveConflictsErrorKey). But it's not clear if it's possible to have this when saving from a child context to a parent context.
There should never be a failure -save: with no error. Every time I have seen that situation it was my fault in my code somewhere.
Can you update the question with showing the code around the error location?
The possible failures of a save are:
Merge failure (most common)
Validation failure (less common)
No store defined in the NSPersistentStore
A nil NSManagedObjectContext (this would present the case you describe)
All of those except for the last will produce an error object to interrogate.

"Core Data could not fulfill a fault for..."

My app is in App Store now. From 2 users I got a lot of crashes. I track them with Crashlytics. The screenshots from my issue are following:
Core Data could not fulfill a fault for... WLWishlist
What does it mean? Is it sth wrong with WLWishlist or objects with relationships to WLWishlist?
Can you help me debug this?
Below is the line 82 for the file when the crash appeared:
That NSObjectInaccessibleException should give you a clue, as should the CoreData could not fulfill a fault for ... /WLWishlist/.... Somewhere you create an instance of a WLWishlist which is an NSManagedObject subclass. You might be creating this instance directly or perhaps it is a related entity from some other managed object. This particular instance is a fault; it's properties have not yet been loaded from the persistent store but should be retrievable on demand. It is expected that your persistent store will be able to fulfill any fault and provide the values of that object's properties. In this case that was not possible and so the persistent store throws an exception.
One way this might happen is if you attempt to fulfill a fault for an object which has been deleted from the persistent store. Without knowing more about what your application is doing it's impossible to say how you got into this situation.

EXC_BAD_ACCESS on mergeChangesFromContextDidSaveNotification

We have been trying to debug a Core Data multiple-context/threading issue wherein merging a Core Data save notification into our main thread NSManagedObjectContext is sporadically crashing the app. This is crashing ~2% of our app sessions and we are at a loss as to how to solve this. We would really appreciate any guidance or general advice on what could possibly cause this crash.
We have a Core Data setup that looks like this:
N.B. This is the default Core Data stack in Magical Record v2.3 created from [MagicalRecord setupAutoMigratingCoreDataStack]
This is the scenario where our app is crashing:
HTTP request returns JSON
JSON is parsed into NSManagedObjects (Some new entities, some updated entities) on Root Saving Context
Root Saving Context saves to persistent store
NSManagedObjectContextDidSaveNotification is broadcast by Core Data. Default context on main queue observes this and calls mergeChangesFromContextDidSaveNotification: with the NSDictionary of changes on the main thread.
It crashes when objectID is sent to an invalid object (most likely NSManagedObject has been deallocated).
This is occurring inside the private implementation of NSManagedObjectContext mergeChangesFromContextDidSaveNotification: so it is impossible for us to see what has actually gone wrong here; all we can tell at this point that an object which should exist, does not.
This only happens on a small percent of Core Data saves, indicating that may not be a fundamental flaw in our Core Data → API stack. Moreover, there is no indication that the size or type of the changes (insertions/updates/deletions) in the context changes have any impact on the likelihood of the crash.
The documentation of NSManagedObjectContextDidSaveNotification says that:
"You can pass the notification object to mergeChangesFromContextDidSaveNotification: on another thread, however you must not use the managed object in the user info dictionary directly on another thread. For more details, see Concurrency with Core Data in Core Data Programming Guide."
Maybe this is the issue? I would make sure the object u get from the notification is getting saved on Default Context on the same thread it was posted by Root.
It's been some time now since this question was posted and after rediscovering it I'd like to answer my own question for the sake of others who find this thread.
In my circumstance I had migrated a large code base from sibling NSManagedObjectContexts updated via NSManagedObjectContextDidSaveNotification's. However the problem was not really anything to do with this, even though this did expose the issue.
The real cause of this were places that there were older parts of the code, setup by previous engineers, that had setup KVO on NSManagedObjects and their properties. It transpired that KVO on Core Data entities is in fact a very very bad idea.
More accurately, it appeared that this happened when KVO was setup on entities and either the object, or the target of a relationship on this object was deleted from the NSPersistentStore. This second condition seemed to not be the only cause of the issue, but was definitely a very prominent cause in my situation.
Lesson's learnt:
Use a fetched results controller when you need to. KVO is not a convenient shortcut and you shouldn't avoid migrating dodgy Core Data KVO code to NSFetchedResultsControllers or another sensible alternative as the procrastination will just hurt you.
Multi threaded Core Data is a difficult but very worthwhile skill to become an expert in. Knowing your Core Data stack and the nuances and limitations of Core Data multithreading is absolutely worth all the mental anguish.
One possibility is that your persistent store has become corrupted and is in an inconsistent state. If this happens an error code is generated which Magical Record does not necessarily deal with. This can be the source of a number of difficult-to-repeat apparently-random crashes related to Magical Record (and may or may not be considered a Magical Record bug).
It's worth reading the Magical Record issues threads here (same issue) and here (different issue, but could be similar cause). When I hit these problems I managed to make some temporary patch fixes following various hints in those threads, but ultimately I decided to remove my dependency on Magical Record, and I have had no problems since then.

unexplained behavioural difference between objectWithID: versus existingObjectWithID:

I understand the documented differences between these two calls. However does anyone know the reasons for the following observed behaviour I have noticed:
If I have a parentContext and a temporary childContext where I use the childContext to edit, insert and deleted objects, if use [childContext objectWithID:objectID]; to retrieve a known existing managed object, present in the parent context, it will sometimes give me an object with a fault that on being fired fails and generates an exception. I understand objectWithID: will, by design, always return an object in a faulted state regardless of if an actual managedObject exists for the given objectID. However if the object actually exists in the parent context, I would expect that when any of the properties are accessed, the object will always be successfully retrieved from the parent context (e.g. the fault will be fired) without any problem. If I use [childContext existingObjectWithID:objectID]; I find it does indeed always succeed.
For the record, I have turned off caching on the child context and this same behaviour occurs after [childContext resetContext] has been called - so it's not an artefact of old cached data hanging around that is inconsistent with the parent context.
The documentation alone seems to me to be insufficient to explain this behaviour. I can of course chalk it up to experience and just say "I now know to always use existingObjectWithID: when passing object IDs to my child edit context perform block" but I feel uneasy and would like to understand exactly what is going on here (not least so I can understand if there is any performance impact of using the one as over the other but also to understand what the constraint is so I can ensure there isn't some bad practice I'm unnecessarily implementing in my code and then using a wrong or inefficient call to fix it).
I've seen this behavior in my own code (the stack traces were from Mars, took forever to track down the cause of the issue), and my solution was the same (move from use of objectWithID: in the child context to use of existingObjectWithID:).
In my case, I'd create an object on the parent context, immediately obtain a permanent object ID for it, and ask for a save using UIManagedDocument's updateChangeCount:UIDocumentChangeDone, which will schedule a save at some point in the future. I think this point is key for the discussion.
I'd then perform a network call to obtain XML data pertaining to the newly-created object, and parse and import that data into Core Data. This occurred on a background thread, and I'd pass the object ID to the thread, which used a thread-confined child import context.
Under iOS5, I'd use objectWithID: in the worker thread to fault the new object into the import context. Worked fine, no issues.
Under iOS6, this began to fail, with bizarre stack traces, and the only solution was to move to existingObjectWithID:. Under testing, it appears to be a solid solution with this change in place.
Like you, I've tried to find some definitive statement as to why this works, but I've been unsuccessful in doing so. However, I think the pattern that my code exhibited and the stack traces I was seeing perhaps explains what's happening. I was always seeing a crash attempting to obtain data from the persistent store. I believe that under iOS5, the save I requested via UIManagedDocument was occurring before my child thread got a chance to run, and thus the new object was persisted before I called objectWithID:. It would appear from my crash logs that that is not the case under iOS6; the thread runs before the object has made it to the persistent store, and thus it can't be fetched into the child context yet. My assumption is that existingObjectWithID: is ensuring that any pending SQL I/O is performed before attempting a fetch, and thus the persistent store is consistent when the fetch occurs in my worker thread. I have been unable to find anything definitive to support this, but extensive testing seems to support that that's what's happening.
Having the same issue as the OP, and while I think the exception is by design as per
If the object is not registered in the context, it may be fetched or returned as a fault. This method always returns an object. The data in the persistent store represented by objectID is assumed to exist—if it does not, the returned object throws an exception when you access any property (that is, when the fault is fired). The benefit of this behavior is that it allows you to create and use faults, then create the underlying data later or in a separate context.
in the Apple docs -- that is, the data is NOT in the persistent store yet, it's sitting in the parent context -- it still seems odd that I simply cannot get my realized object in the parent context. Why must the persistent store have to know about it, for my object to be populated?
Getting the "existing object" using existingObjectWithID before getting the "crash object" using objectWithID will lead to both objects being equal.
The other way round the app will crash as soon as a property of the "crash object" is accessed. In this case the objects are not equal. The "crash object" has a temporary ObjectID.

Resources