What happens when you try to save managedObjectContext from a different thread? - iphone

So I know that NSManagedObjects are not thread safe and managedObjectIDs are, and we need a separate managedObjectContext per thread. But recently I had an issue when I was doing some core data changes in the background (had a separate runloop thread for this) and performSelectorOnThread: method sometimes was simply not invoked on this runloop thread. It turned out that the reason was that I was doing
[someObject.managedObjectContext save:&error]
on this runloop thread and "someObject" was created on the main thread. But it would only "hung" runloop thread once in a while. So the question is what really happens if you try to save context in a different thread. I'm just looking for a deeper understanding, thanks.

From https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/CoreDataFramework/Classes/NSManagedObjectContext_Class/NSManagedObjectContext.html :
Core Data uses thread (or serialized queue) confinement to protect
managed objects and managed object contexts (see “Concurrency with
Core Data”). A consequence of this is that a context assumes the
default owner is the thread or queue that allocated it—this is
determined by the thread that calls its init method. You should not,
therefore, initialize a context on one thread then pass it to a
different thread. Instead, you should pass a reference to a persistent
store coordinator and have the receiving thread/queue create a new
context derived from that.

You will crash. Maybe it will work sometimes and you won't see a crash while debugging, but you should never do this. Object contexts and the managed objects in them should only be used on the thread on which they were created. Apple's documentation is very clear about this and gives many examples of how to handle situations where you may have long running operations (slow fetches or async saves). You should read the docs that talk about threading with Core Data for more info.

Related

How to find and cancel background Thread in Swift

I am calling a monitoring task via thread using the following code which is called from the viewDidLoad() in a ViewController:
let myDaemon = Thread(target: self.myMonitor, selector:#selector(self.myMonitor), object: nil)
myDaemon.start()
I have been unable to find a way to find and cancel that thread without closing the app. Is there maybe an alternate way to launch the thread so I can cancel it if needed?
I thought about creating an observer so I could cancel it from another VC but since the Daemon is initialized in the viewDidLoad, I can't access it outside of that block.
I'm using Swift 5. Any suggestions are appreciated.
Thanks,
First, you should almost never use Thread in Swift. Directly accessing threads has been discouraged in Cocoa since longer than Swift has been a language. You should generally be using, in order of preference for the kinds of problems threads are usually used for, async/await (possibly plus an AsyncChannel), OperationQueues, or DispatchQueues. That said, it's a fine question, and there are still very rare cases where a Thread would be appropriate, or at least useful.
To cancel a thread, you will either need to keep track of it (the returned value) so you can call .cancel(), or you need to have a way to communicate with the thread (a Pipe for example) so that you can send a custom "stop" message. That means storing that returned value (or the communication mechanism) somewhere outside the VC.
A ViewController should not own a global object like a Thread. ViewControllers come and go. You should move your thread management (if you're going to do thread management, which you generally shouldn't) into a model object that the VCs share.
Note that canceling a thread does not cause a thread to stop running. The only thing it does is set the isCancelled flag. If is still up to your thread to periodically check itself for cancellation and stop. (You may already know this, but it's a very common confusion, so I want to make sure anyone reading this later is aware.)
There is no list of all existing threads for you to search (and that would be pretty obnoxious since the frameworks generate quite a lot of threads you would need to crawl through). If you want to keep track of a thread, you need to store it somewhere.

Core Data - sharing NSManagedObjects among multiple threads

I suffered all the consequences of using a single MOC in multiple threads - my app crashes at random points because the MOC is created in the main thread and I also use it to fill the DB in another thread.
Though the code is synchronized (#synchronize) using a global singleton the app crashes.
I read that using a separate MOC for each thread will make things ok but I also read that it is considered also a bad approach to share NSManagedObjects across threads.
My use case is the following:
1)I load and parse XML from a server and during the parsing I insert each new NSManagedObject in the database. This all happens in a separate thread.
2)From the main thread the user interacts with the UI which reads data from the database.
In both threads I use NSManagedObjects. How would you suggest me to fix this? I failed multiple times already.
Most often the app creashed with error suggesting that I am modifying a collection while enumerating it which is not true as the code is synchronized and while I am iterating it no modifying happens and vice versa - while I modify it I don't iterate and I save once I am done.
Use one NSManagedObjectContext per thread. If you communicate between threads, pass the NSManagedObjectID, which is thread safe, and fetch the object again from you thread context. In my apps I sometimes even use one context per controller.
To manage the different contexts, register an Observer for the NSManagedObjectContextDidChangeNotification. Within this notification handling, you pass the notification to each of your contexts via the mergeChangesFromContextDidSaveNotification: method. This method is thread save and makes the context update its state.
After this you have to refresh your views. If you have a table view based application, have a look at NSFetchedResultsController. This helps you update the table automatically with appropriate animations. If you don't use table views, you have to implement the UI update yourself.
If you are only supporting iOS 5 and above you don't need to deal with NSManagedObjectID and merging contexts anymore. You can use the new concurrency types of NSManagedObjectContext instead. Then do your operations within managedObjectContext:performBlock and they will be merged automatically.
See the answer from svena here for more information:
Core Data and Concurrency using NSOperationQueues

Core Data: fetch background and using objectWithID on main thread, performance benefit?

I read Core Data Programming Guide recently and Apple suggest us to do so
You fetch in one managed object context on a background thread, and pass the object IDs of the fetched
objects to another thread. In the second thread (typically the application's main thread, so that you can
then display the results), you use the second context to fault in objects with those object IDs (you use
objectWithID: to instantiate the object). (This technique is only useful if you are using an SQLite store, > since data from binary and XML stores is read into memory immediately on open.)
To my understanding, fetch on background context will not register the managed object in main thread context, so the managed object returned from objectWithID is most likely a fault. When we using it on main thread, we will trigger a new round of trip to the SQLite Store. So the UI maybe blocked.
Did I miss anything ? Are there a way to avoid I/O overhead on main thread ?
There's not much overheard when you do a fetch in the background and then use the objectID to do a fetch on the main thread. First, the record will be on the CoreData cache, which makes the same fetch on the main thread faster, and second, fetching with an objectID is much much much faster than fetching with your average fetch request. What you usually do is that you would create a background fetch request, find the objectID of the objects you are looking for, and move those objectIDs to the main thread. Of course, for the background thread, you have to use a different NSManagedObjectContext instance than the main thread one.
I would recommend you to check the WWDC 2010 video "Mastering Core Data". It goes into core data and multithreading, explaining the performance of caching and fetching on the background/main thread.

iPhone programming - Background saving with core data

I am trying to save data into core data in the background thread, as it takes quite some time to save.
I did:
[self performSelectorInBackGround:#selector(insertRecord:) withObject:data];
When all works fine until the line in insertRecord Method hits contextsave:&error. Program received signal : "SIGABRT"
Am I doing anything wrong? it works ok when its in main thread, I just move the codes to another method and run it in background and it doesn't work anymore.
According to the "Concurrency with Core Data" section of Core Data Programming Guide:
The pattern recommended for concurrent programming with Core Data is
thread confinement: each thread must have its own entirely private
managed object context.
and
Using thread confinement, you should not pass managed objects or
managed object contexts between threads.
It looks like you're passing a managed object to the background thread, which is forbidden. I don't know if you're also trying to share your managed object context between threads.
That document describes a couple of workarounds for passing managed objects to other threads. You'll need to implement one of them.
The problem here is that managed object contexts aren't thread-safe. If your -insertRecord: method uses the main thread's managed object context, you're asking for trouble.
The blog Cocoa Is My Girlfriend has an article, Core Data and Threads, Without the Headache on this very topic and suggests some strategies for saving in the background. The basic idea is to make changes on a context that belongs to a background thread, and then merge the changes into the main thread's context. That gives you an up-to-date context that you can save in the background while still keeping the main thread's context current.

Class which handles tasks in thread

I want to make a class which as long as an instance of it is alive, keeps a thread (worker) going and when someone calls a method on it - performTaskWithData:(NSData*)data - then it should process this data in its worker thread.
If additional data is sent while an operation is taking place, then this new data/operation should be queued until the previous processing is done.
I need each instance of this helper class to hold one single worker thread (i.e. the same thread should handle all the processing).
How should I go about doing this?
NSRunLoop? Synchronize access to data block being passed?
Starting in iOS4, Grand Central Dispatch provides by far the simplest and most powerful interface to multithreaded programming.
If you're a registered developer, go watch some of the WWDC videos from 2010 about it. It's intimidating at first, but it's actually really simple and good.
You can do this directly with NSThreads and run loops. However, I would consider using NSOperationQueues, one per instance of your class and set the maximum concurrency of the queue to 1. Your performTaskWithData: would simply add a new instance of a subclass of NSOperation to the queue and that's it.