Some unkown NSManagedObjectContext - ios

I'm working a multithread core data project. I set up the core data stack as follows:
Create Only One persistStoreCoordinator
Create Two NSManagedObjectContext, both set their coordinator to persistStoreCoordinator, so these two contexts(one is main context of main current type and another is background context of private queue type) are in same level, not parent-child hierarchy.
This core data stack is hold by my Singelton class. So init only once.
After doing this. I create a view controller to test this. However, even when I do nothing on core data stack. I found the callback for NSManagedObjectContextDidSaveNotification are triggered. And the notification.object(which is a NSManagedObjectContext) is neither my main context nor my private context. And its persistCoordinator is not the same as the one I created.
So does anyone met this problem before or Can anyone of u give me some idea to track this problem?
Thanks in advance.

Related

How does Core data concurrency work with multithreading in Swift 3?

In my program, it uses both of
DispatchQueue.global(qos: .background)
and
self.concurrentQueue.sync(flags: .barrier)
to deal with the background multithread issues.
It is swift 3 so I use the latest way to get the childContext:
lazy var context: NSManagedObjectContext = {
return (UIApplication.shared.delegate as! AppDelegate).persistentContainer.newBackgroundContext()
}()
I also enable -com.apple.CoreData.ConcurrencyDebug 1 to debug
Then the problem occurs:
1, When there's an API call and in the callback block (background thread), I need to fetch the core data, edit, then save. I tried to use self.context from the code above to call performBlockAndWait and do save inside of this block. The whole process goes fine but when I try to access my result outside of this block but inside of the callback block, the error occurs. I have also tried to get the objectId and getObjectById by both self.context and self.context.parent and the error occurs on this line. What did I do wrong and how should I do this? since I need to use the result everywhere in many different thread (not context).
2, I read a post says that I need one context per thread, then in my case, how do I determine which exact thread it is if it's a call back from API call and do I really need to do this?
3, You might ask that why do I need a privateConcurrentType, because my program has things need to be running in background thread so that it has to do it this way, (read from other post), is this right?
4, Even in my question 1, get object by passing objectId to different Context still not working in my case. Let's assume this is the proper way. How am I gonna manage passing so many objectID throughout my entire program in different thread without being super messy? To me this sounds crazy but I suppose there's a much cleaner and easier way to deal with this.
5, I have read many posts some are pretty old (before swift 3), they have to do childContext.save then parentContext.save, but since I use the code above (swift 3 only). It seems that I can do childContext.save only to make it work? Am I right?
Core data in general is not multithreading friendly. To use it on concurrent thread I can assume only bad things will happen. You may not simply manipulate managed objects outside the thread on which the context is.
As you already mentioned you need a separate context per thread which will work in most cases but by my experience you only need one background context which is read-write and a single main thread read-only context that is used for fetch result controllers or other instant fetches.
Think of a context as some in-memory module that communicates with the database (a file). Fetched entities are shared within the context but are not shared between contexts. So you can modify pretty much anything inside the context but that will not show in the database or other contexts until you save the context into the database. And if you modify the same entity on 2 contexts and then save them you will get a conflict which should be resolved by you.
All of these then make quite a mess in the code logic and so multiple contexts seem like something to avoid. What I do is create a background context and then do all of the operations on that context. Context has a method perform which will execute the code on its own thread which is not main (for background context) and this thread is serial.
So for instance when doing a smart client I will get a response from server with new entries. These are parsed on the fly and I perform a block on context to get all the corresponding objects in the database and create the ones that do not exist. Then copy the data and save the context into database.
For the UI part I do similar. Once an entry should be saved I either create or update the entity on the background context thread. Then usually do some UI stuff on completion so I have a method:
public func performBlockOnBackgroundContextAndReturnOnMain(block: #escaping (() -> Void), main: #escaping (() -> Void)) {
if let context = context {
context.perform {
block()
DispatchQueue.main.async(execute: { () -> Void in
main()
})
}
}
}
So pretty much all of the core data logic happens on a single thread which is in background. For some cases I do use a main context to get items from fetch result controller for instance; I display a list of objects with it and once user selects one of the items I refetch that item from the background context and use that one in the user interface and to modify it.
But even that may give you trouble as some properties may be loaded lazily from database so you must ensure that all the data you need will be loaded on the context and you may access them on the main thread. There is method for that but I rather use wrappers:
I have a single superclass for all the entities in the database model which include id only. So I also have a superclass wrapper which has all the logic to work with the rest of wrappers. What I am left with in the end is that for each of the subclass I need to override 2 mapping methods (from and to) managed object.
It might seem silly to create additional wrappers and to copy the data into memory from managed object but the thing is you need to do that for most of the managed objects anyway; Converting NSData to/from UIImage, NSDate to/from Date, enumerations to/from integers or strings... So in the end you are more or less just left with strings that are copied 1-to-1. Also this makes it easy to have the code that maps the response from your server in this class or any additional logic where you will have no naming conflicts with managed objects.

Core Data Multiple ManagedObjectContext

How the multiple ManagedObjectContext (MOC) works in core data(Swift 2, iOS 9). I have been through lots of links & material online and answers on StackOverflow, but couldn't find exact answer.
I want to know, suppose I have created main MOC which points to PersistentStoreCoordinator (PSC)and another private queue MOC which has parent context set to above mentioned main MOC.
Question 1: Then How this works ? Is the hierarchy built like this : Private queue MOC --> Main queue MOC --> PSC.
Question 2: If I call save on 'private queue MOC', will it save to main MOC and in turn automatically main MOC will save to PSC ? or After save on Private MOC we have to call explicit save on Main MOC to save it to PSC ?
I just started working on core data and online links are not so clear. So, any simplified explanation will be much appreciated.
Thanks!
For question 1, the hierarchy is however you create it. If you create a context with no parent, and then another one whose parent context is the first context, it'll be as you describe.
For question 2 I'll turn to the documentation on NSManagedObjectContext:
When you save changes in a context, the changes are only committed β€œone store up.” If you save a child context, changes are pushed to its parent. Changes are not saved to the persistent store until the root context is saved.
Saving is never automatic, so the parent context doesn't save changes until you tell it to save.

When to init the NSManagedObjectContext?

I have an iOS app with master and detail layout.
In master, I managed my own NSManagedObjectContext, and also detail is, by this way:
NSPersistentStoreCoordinator *psc = ((AppDelegate *)UIApplication.sharedApplication.delegate).persistentStoreCoordinator;
_context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_context setPersistentStoreCoordinator:psc];
In master, I display list that can be clicked by user to show the detail in detail layout.
Upon filling the detail by user, user can save the detail by clicking on button there.
However, I am trying to understand this:
Since there is a save button in detail, the save method will save the detail with detail's context and call the load list in master
Load list will remove all the NSMutableArray of the list [_activities removeAllObjects]; and re-fetch the data from Core Data and reload the tableview
Done that but the the re-fetch function seems to use old data and not the latest.
Why does re-fetch the data doesn't work if I use same context?
You are using outdated APIs to create your managed object contexts. After creating a context, instead of assigning the persistent store, you should set the parentContext.
If you display a "list", you should be using a context that is of type NSMainQueueConcurrencyType. The best way is a NSFetchedResultsController which will also help you manage memory and performance, and greatly simplify the updates you need for your UI. (You would avoid the verbosity and complexity of merging the data "manually" via NSManagedObjectContextDidSaveNotification.)
NB: Resetting the context is a crude and inefficient method for the task you are trying to accomplish. Due to the fact that it affects the entire context, it could change the behavior in other parts of your app.
You have two contexts going on here which is why the data is not being refreshed. If you call [context reset] then you will find that the data will refresh.
Explanation:
An NSManagedObjedctContext is a "scratchpad" which manages the Objective-C representation of SQLite (or XML) data in memory. When you fetch data the context retains the managed objects that are created by the fetch. If you then fetch from another context that context reads the data from the persistent store and creates new managed objects based on what it finds.
ALSO: When you perform a fetch the context checks to see if a managed object representation is ALREADY in existence and then returns it if it is. This means that two contexts can get out of sync quite quickly.
There are several ways around this:
1) Calling reset on a context returns it to it's "baseState" which means all the managed objects are "forgotten" or released by the context. Therefore, another fetch will force the context to read the data directly from the store.
2) Implementing the NSManagedObjectContextDidSaveNotification (see here) will allow you to incorporate changes between contexts.
I have found this article very useful in the past.

CoreData: Private context with child Main context - FetchedResultsController not getting updates

I've been trying to build a Core Data stack as described by Marcus Zarra, where he has a Private Queue context and a Main Queue context (where the Main Queue context is a child of the Private Queue context).
I believe I've correctly rebuilt (here) his described MCPersistenceController faithfully in Swift (the example code was Obj-C).
The problem is in my ListViewModel class (which contains an NSFetchedResultsController). No matter what I try, its delegate callbacks (controllerDidChangeContent etc) don't get called when a new Item object is inserted.
The FRC and NSFetchRequest use the Main Context; the item is inserted on the main context; the save function save the Main Context and then merge the changes into the Private Queue
If I perform a manual executeFetchRequest on either context after inserting and saving, I do get the newly created Item back.
If I listen for NSManagedObjectContextObjectsDidChangeNotification notifications, they're indeed triggered after inserting.
When I relaunch the app, my inserted Items are now shown.
I can only assume it's an issue with doing something on the wrong thread and there being no reported error, but no matter what I've tried, when I insert a new Item the FRC triggers no delegate callback. Possibly it's some Swift thing that I've missed.
I'd really appreciate any suggestions at this point 😏.
My simple proof-of-concept project (Swift 1.2) is on GitHub. (I didn't get to the CloudKit stuff yet..).
Your ListViewModel object is a pure Swift object. The fetched results controller uses NSObject-descended functionality to check if the delegate responds to the delegate methods.
#objc
class ListViewModel: NSFetchedResultsControllerDelegate{
Fixes 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