Infrequent CoreData crash "nilOutReservedCurrentEventSnapshot" - ios

There is a crash occurring within my app that only happens quite rarely (maybe once every 30 runs). The error code contains a strange selector name _nilOutReservedCurrentEventSnapshot__ which I haven't been able to find any documentation for at all. Here is the feed from my console:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType _nilOutReservedCurrentEventSnapshot__]: unrecognized selector sent to instance 0x157b51e0'
*** First throw call stack:
(0x2358810b 0x22d2ee17 0x2358d925 0x2358b559 0x234bbc08 0x24cbf445 0x24ca4d99 0x249bec 0x245c90 0x19b68c 0x24a5c97 0x24b05ab 0x24a8ef9 0x24b1a8d 0x24b18e7 0x232bfb29 0x232bf718)
libc++abi.dylib: terminating with uncaught exception of type NSException
If anyone is able to shed some light on the meaning of this phrase _nilOutReservedCurrentEventSnapshot__`, that would help me immensely. A screenshot of the location of the crash is below:

Unfortunately there isn’t much you can find about _nilOutReservedCurrentEventSnapshot__
online..
It is probably related to the snapshot lifecycle of a managed object.
When Core Data fetches an object from a persistent store, it takes a snapshot of its state. A snapshot is a dictionary of an object’s persistent properties—typically all its attributes and the global IDs of any objects to which it has a to-one relationship. Snapshots participate in optimistic locking. When the framework saves, it compares the values in each edited object’s snapshot with the then-current corresponding values in the persistent store.
The fact that _nilOutReservedCurrentEventSnapshot__ is not documented means that this type of behaviour is not supposed to happen.
What we do know is that it is a function of the NSManagedObject class.
Therefore The error unrecognized selector sent to instance was caused because _nilOutReservedCurrentEventSnapshot__ was called on an object that was not an NSManagedObject, because the NSManagedObject was deallocated and something else now fills its memory. This is fact.
Context isn’t given from the question about the nature of the app and its CoreData setup but it is inferred that it is using a parent-child concurrency pattern. This is important.
[Image retrieved from here]
From all the questions asked on stack overflow that I can find about this error, it appears they all use the parent-child concurrency pattern.
It is entirely possible that the issue is caused by the adoption of this pattern with improper implementation or handling of ManagedObjects.
A Situation where a parent-child context may be used is when syncing cloud data or handling changes made when a user edits something with the option of discarding or undoing the changes made, which can be done on a background thread.
It is also mentioned in the question that this is a rare occurrence and doesn’t happen every time. Meaning that the contexts are probably fine until a certain save or change is made and somehow the contexts become out of sync, and performing another save crashes the app.
From the docs on Synchronising changes between contexts:
If you use more than one managed object context in an application, Core Data does not automatically notify one context of changes made to objects in another. In general, this is because a context is intended to be a scratch pad where you can make changes to objects in isolation, and you can discard the changes without affecting other contexts.
Changes will be sent to the parent context when the child is saved but not if the parent has been changed separately. It is strongly adviced you never change the parent context; only through saving the child context and propagating the changes from there.
Possible reason for crash
There are several things that may cause this but two that stick out are:
1. deallocation of ManagedObject from reference that is disposed of due to dismissing a modal view in an iOS application or sheet in an OSX application.
possible solution: set the retainsRegisteredObjects property of the child context to true before fetching ManagedObjects (and then setting to false once the contexts have been saved to avoid further potential memory leaks). warning!
eg. ctx.retainsRegisteredObjects = true
2. Unhandled changes to a context.
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/ChangeManagement.html#//apple_ref/doc/uid/TP40001075-CH22-SW1
Consider an application with two managed object contexts and a single persistent store coordinator. If a user deletes an object in the first context (moc1), you may need to inform the second context (moc2) that an object has been deleted. In all cases, moc1 automatically posts an NSManagedObjectContextDidSaveNotification notification via the NSNotificationCenter that your application should register for and use as the trigger for whatever actions it needs to take. This notification contains information not only about deleted objects, but also about changed objects. You need to handle these changes because they may be the result of the delete. Most of these types of changes involve transient relationships or fetched properties.
Possible solutions here are:
Use block methods on your contexts. refer to callback function at bottom of linked file.
Make sure to use objectWithID when passing around ManagedObjects between contexts.
Taking appropriate action on object change using a NSManagedObjectContextDidSaveNotification trigger.
removing all strong references to a ManagedObject. It may be possible that a strong reference was made to a ManagedObject used outside of the scope of the given code and so when the parent deallocated the ManagedObject and the child didn't, on a future save the child performed another save on that ManagedObject and then the parent context tried saving the ManagedObject which was deallocated.
"it is a good pattern to try to fetch objects, modify them, save them and reset the edit [child] context"
Because there is no other information here to deduce what is causing the error, and I can’t reproduce it.. I would suggest trying to find where your ManagedObject is deallocated using Zombie profiling in Instruments.
If a message is then sent to one of these deallocated objects (which are now NSZombie objects), the zombie is flagged, the app crashes, recording stops, and a Zombie Messaged dialog appears. You can then examine the retain and release history of the zombie object to determine exactly where the problem occurred.
https://github.com/iascchen/SwiftCoreDataSimpleDemo/blob/master/SwiftCoreDataSimpleDemo/CoreDataHelper.swift
Hopefully then someone can shed more light on what caused the issue.
+++++++++++++++++++++++++++++++++
Side Note:
parent/child contexts could result in stuttering from the UI - “every fetch request and every save operation will fully block the main thread while data is read or written to/from disk”. Which is because every fetch is pulled through the parent context (on the main thread) to the child.
supporting SO answer
It is recommended that if you are using this approach that you rethink the design to something like two independent managed object contexts connected to the same persistent store coordinator Which might 'avoid' the issue at hand in future from happening.
[Image retrieved from here]
You can see the drastic performance differences in the linked article

_nilOutReservedCurrentEventSnapshot__ is a private method on NSManagedObject internally to flush attributes and values from a Core Data object. You can see it in the private header for NSManagedObject here.
__NSCFType is an private wrapper for Core Foundation types used internally by the Objective-C runtime. You can learn more by looking at the private header here, but there isn't much to see.
Without the full backtrace, it's hard to debug the specific issue. The way I see it, there could be two culprits:
The parentObject of your context is somehow invalid.
You're trying to save a Core Foundation object as a property on an NSManagedObject which isn't expecting it.

A wild guess.. Is there any chance to call this save method from a different thread than the actual thread who created the context? For safer side, always execute Save: operation from perform / PerformBlockAndWait.

Related

Core Data: Illegal attempt to establish a relationship 'statusmedia' between objects in different contexts

I am using Core Data on my app. After a light weight migration and code upgradation to swift 4.0, I am facing few issues.
Issue 1
Mutating a managed object 0x7fd0881de320 (0x7fd0884589b0) after it has been removed from its context.
Issue 2
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Illegal attempt to establish a relationship 'statusmedia' between objects in different contexts (source = (entity: Status; id: 0x10f6dc280
Termination issue is very serious.
Note that I have used only one Context (default one and not private)
Let me know if you want more information from me...
Any help would be greatly appreciated.
Many may happen unfortunately to lead to this but top candidates to check would be:
In your model there are delete rules on relations. A "cascade" option should delete the object(s) in relation when deleting this object. If this is not expected such an error could easily be produced.
You are deleting an object but then still use it. It is a logical bug but can be easily confirmed by marking deleted managed objects (put the IDs of deleted objects into some array) and then checking if the failed object is marked (it's ID exists in that array).
You are using multiple contexts but not aware of it. Some tools like fetch result controllers are potential candidates.
There are still other possibilities but I would start with these mentioned to begin with.

UIManagedDocument parent context object insertion on a background priority queue not updating in UI using child context

I'm trying to implement some basic UIManagedDocument import/export functionality into my app, mainly for dev so that I can easily inspect the document contents and more crucially preserve a set of test data when I start trying to iterate on my CoreData models.
All I am trying to do is load some JSON data from a local file and inject it into my apps UIManagedDocument. The UIManagedDocument's ManagedObjectContext contents are visualised in my app using some Core Data Table View Controllers from the Stanford iOS courses.
I thought I'd try to write this with some threading to keep the UI responsive and to learn how to do it. So I've done something like this
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// read JSON data file
// parse JSON into dictionaries etc
// iterate over dictionaries
// try to create a new core data entity with my document's ManagedObjectContext
} );
At first I thought this was working. No errors, asserts, crashes triggered and upon looking at my CoreData TableViews I could see the newly added objects in my UI. Unfortunately the newly added objects were seemingly never saved back to the store. I even hooked up to listen to the NSManagedObjectContextDidSaveNotification from my UIDocument's managedObjectContext and saw it wasn't triggering on pressing the home button, like it usually does if it has some changes performed in the app with my UI pending. Infact even doing these operations in the UI wouldn't cause the notification and saving to occur so it was clearly not happy.
I unrolled the code from within the background queue and ran it on the main thread synchronously and everything worked ok, the new data was saved correctly.
I started reading about the complexities of threading and coredata, the documentation seemed to suggest using the UIDocument's ManagedObjectContext's parent ManagedObjectContext to perform operations on in the background so I tried doing the same code again using this parent context, so as follows
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// read JSON data file
// parse JSON into dictionaries etc
// iterate over dictionaries
// try to create a new core data entity with my document's ManagedObjectContext parent ManagedObjectContext
} );
This time for some reason the CoreData TableView controllers no longer updated to show the newly injected objects. Even after explicitly calling save on the parent context, nothing appeared. However on quitting the app and reloading the app the newly injected objects did seem to be added correctly. Interestingly at this point i'd left a fetchrequest with a cachename specified and that threw up an error on this first run of the app after injecting the objects this way. I guess somehow the way the object had come from the parent context directly perhaps invalidated the cache somehow, that's still something I don't fully understand. Even changing the cache to nil didn't fix the issue of the table views not updated the same session as when the objects were injected into the parent context.
Looking elsewhere I've seen some uses of the managedObjectContext performBlock suggested. Another case where someone has said you must call
[document updateChangeCount:UIDocumentChangeDone]
after all changes to ensure the saving is performed, or perhaps using
- (void)autosaveWithCompletionHandler:(void (^)(BOOL success))completionHandler
instead. Though elsewhere I've seen mentioned that saving should be enough to push context contents through the hierarchy. Does saving only work from child -> parent and not from parent -> child.
Or am I just doing it wrong?
Anyone's time and help is really appreciated! Cheers.
please look at the 'Parent/Child Contexts' section in the Multi-Context CoreData.
Whenever a child MOC saves the parent learns about these changes and this causes the fetched results controllers to be informed about these changes as well. This does not yet persist the data however, since the background MOCs don’t know about the PSC. To get the data to disk you need an additional saveContext: on the main queue MOC.
I do saving to the PSC n following way:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
{
// read JSON data file
// parse JSON into dictionaries etc
// iterate over dictionaries
NSError* error;
if (self.managedObjectContext.hasChanges)
{
[self.managedObjectContext save: &error];
}
// Save main context
dispatch_sync(dispatch_get_main_queue(), ^
{
[[AXContextHelper sharedInstance] saveMainContext];
});
}
After looking into all the suggestions I could find for similar problems, none of them seemed to help.
The scenario i was describing in the end had the store stuff to file handled ok, but the UI not updating. At the time when I do this import/export I'm in a view ontop of the core data table view controller that doesn't update when I inject all these JSON objects, so in the end all I did was force that controller to re-fetch it's data. I believe the NSFetchedResultsController is meant to monitor the managedObjectContext and update the fetch request as required. For whatever reason, this wasn't working.
With the force re-fetch called, everything seems to work ok. The newly injected entities appear in my tables, and are saved to the store files.
Either there are some bugs in these objects, or I'm still using it wrong but I see other problems... my import/export data both work now on background threads with the parent context. What I've noticed today is that I can use the UI to properly insert and edit some objects using the child context on the main thread. I don't deliberately call anything at the moment after making these edits so i guess the edits are are still pending till core data decides to save. If i then go to my export feature and use it using the parent context then i find the newly edited or inserted objects aren't there.
Clearly there's no messaging happening in time from child -> parent, and messaging from background thread edits on the the parent itself just doesn't seem to work.. I never get any notifications for the edits i perform in a thread on the parent context, even though they seem to work. If I force the save to happen via the documents then the passed in dictionaries show no edits being made, even though the edits are saved correctly to store files somehow.
I guess i now need to force my document to save after every operation, or at least before every operation i'm about to do on another thread to ensure the parent/child are both in sync.
Still the problem I originally described I've managed to get around, but I'm still left wondering quite how I should be using the parent and child context of a UIManagedDocument to avoid encountering these issues.

RestKit: Managed object mapped inside a plain old Objective-C object

Preamble: I'm using RestKit 0.21.0, but I've also tried 0.23.0.
I have a number of mappings where a plain old Objective-C object (let's call that a POOCO) contains an NSManagedObject. It seems that when doing GET requests on this root POOCO, the managed object is created under the mainQueueManagedObjectContext.
However when I do a PUT against the POOCO, when the response is loaded (the response is a copy of the object that was sent), the managed sub-object is created under a temporary NSManagedObjectContext, whose parent is the mainQueueManagedObjectContext.
A quick aside on re-fetching in RestKit:
If the request were composed entirely of NSManagedObject instances, the resulting objects would be "refetched" using (I think) the mainQueueManagedObjectContext. (IIRC, once all the object mapping is complete, the objects and sub-objects would all be re-fetched from the main MOC, replacing the objects that were created on the temporary MOC used for mapping.)
This refetching is done (as far as I can tell) because when a MOC gets dealloc'd, all of the managed objects that it manages become invalid. So we refetch the objects in a MOC to which myObjectManager.managedObjectStore maintains a strong reference, so that they remain valid after the temporary MOC goes away.
Because the root object in the mapping is a POOCO, any references it has to managed objects do not get refetched, so they remain attached to the temporary MOC, and become invalid once mapping is complete.
Has anyone else run into issues like this? Are there best practices you can suggest? Is it possible to tell RestKit, "refetch these managed objects using the mainQueueManagedObjectContext" or anything like that?
Managed object instances can't be passed between threads, and RestKit does the mapping on background threads. If the returned objects are managed objects then they will be transferred to the main thread for you. It could be argued that nested managed objects not being switched to the main thread for you is a bug / oversight in RestKit (that you could raise as an issue in github).
You can ask RestKit to use the main thread for some things (by explicitly creating the operations and running them, though this doesn't cover everything) but you really don't want to do that.
So, your main option is to have a method in your container object which you can call in the success block and which iterates through the contained managed objects and 'refetches' them - as you know that all the objects exist this can be done with objectWithID:, and the success block is called on the main thread.
The question actually has some misconceptions about when RestKit performs refetching. Under the circumstances described in the question, RestKit actually does return an instance of RKRefetchingMappingResult in the success block. Calling any of the accessors on that object (such as array or firstObject) actually does perform the refetching. The mistake was that I was never invoking any of these methods on the RKMappingResult.
My assumption was that, since the object I was sending in the PUT request would become the operation's targetObject, the object would be updated in-place by RestKit. This is why I never felt the need to do anything with the RKMappingResult. The assumption isn't technically wrong, but by accessing the object directly once the mapping was complete instead of using the RKMappingResult, I was skipping the refetching step.
I've proposed a change in this behaviour in an issue I've filed with the RestKit folks, to make refetching automatic in certain circumstances.
To summarize:
When making a request using RKManagedObjectRequestOperation (whether you specify that manually or RestKit selects this class for you), always make sure you exercise the RKMappingResult to ensure that the results are re-fetched from the correct NSManagedObjectContext.

iOS5 NSFetchedResultsController not getting delete updates

I have a NSFetchedResultsController tied to my main managed object context. It is in charge of keeping data for a table view in my main view.
I have an NSOperation running on a background thread that refreshes/deletes the managed objects that the fetched results controller is keeping track of. I create a child context (private concurrency type / parent = main managed object context) in the nsoperation and insert/delete objects. When it is finished, it saves its context, as well as the parent context.
What's interesting and very frustrating is that this works fine in iOS 6. When I insert or delete objects, my fetched results controller is notified of the changes and everything works as expected. However, on iOS 5, everything works except for deletes. The fetched results controller is not notified of a delete. However... if I manually refresh the fetched results controller (making it nil and refetching the same predicate) then it will show the expected result.
Also, when I register for change/save notifications on the main context, I can see that the user info dictionary contains the objects that I've deleted... even in iOS 5!
One issue that I thought it may be, but I don't think holds because I've removed the factors, is that this object is in a many to one relationship with another object. The object I am deleting/inserting is an "employee" and it has a relationship with a "department". The employee is set to nullify and the department is set to cascade.
As I said, this works fine in iOS6 but not in iOS5.
Any tips would be very helpful.
This bug is due to saving to the persistent store. This child context saves itself, then calls perform block on it's parent, the main managed object context. When the main managed object context saves, it triggers a background context to write to the persistent store. When I removed the background context save, the fetched results controller updated as expected.
Something interesting that I found that was probably causing this was that the managed object was leaking every time I tried saving to the store. Not exactly sure how to fix this yet, but it's good to know the reason for it.

Core Data crash (EXC_BAD_ACCESS) on synchronizing contexts in mergeChangesFromContextDidSaveNotification

I'm writing an iPad cookery application which is heavily based on CoreData.
In my app I have a screen for editing recipe with 2 types of information:
Lists with recipe category\subcategory and country
All other recipe-related information
Each of this list may be edited in popover. Changes of this list shall be persisted immediately (i.e. if user will add some recipe category to possible categories list, but cancels recipe creation, this category shall be available for all recipes).
It was descided to implement 2 separate NSManagedObjectContexts to handle each type of info: main context for recipes management and supporting context for lists.
All core data operations performed via MagicalRecord framework. Both of contexts having MagicalRecord's default context as parent. Each context observes changes in other one. All contexts are being created and used on main thread so it seems that this issue has nothing related to multithreading issues.
When I'm trying to create object in support context and persist changes in support context immediately after object's creation everything going OK. The problem comes when newly created object is being deleted right after creation - EXC_BAD_ACCES received.
However, the entity is being persisted correctly and on next launch it may be used and deleted without synchronization issues.
1 note: When object is being accessed from Main context by existingObjectWithID: method of NSManagedObjectContext it becomes possible to delete this object. However crash happens on main context's (parent context of both Main and Supporting contexts) saving then.
Here is my code:
Entity creation:
RecipeCategory* category = [RecipeCategory MR_createInContext:_supportingContext];
category.name = itemName;
[_supportingContext MR_saveToPersistentStoreAndWait];
Entity deletion:
[(RecipeCategory*)itemToRemove MR_deleteEntity];
[_supportingContext MR_saveToPersistentStoreAndWait];
Contexts creation and observing setup:
[_mainContext MR_stopObservingContext:_supportingContext];
[_supportingContext MR_stopObservingContext:_mainContext];
_mainContext = [NSManagedObjectContext MR_contextWithParent:[NSManagedObjectContext MR_defaultContext]];
_supportingContext = [NSManagedObjectContext MR_contextWithParent:[NSManagedObjectContext MR_defaultContext]];
[_mainContext MR_observeContextOnMainThread:_supportingContext];
[_supportingContext MR_observeContextOnMainThread:_mainContext];
Please advice what may cause this issue because now I'm confused even in which way shall I move to solve this. Changes management section in Core Data documentation gives nothing. Same results gave google.
Don't do that. A context watching another that is also watching the watcher...that's bad. You first need to understand the rules of nested contexts and how data flows from one to the other on save.
I your case, you may want to look for the MR_confinementContext method on NSMOC. This will create a context that uses the old thread confinement model. This may help you get around your thread crashes. But first, don't do the circular observing...data flows in

Resources