CoreData in one single separate thread - xcode

What I want in short is:
Core Data that runs without blocking the main thread
entities with relationships
bindings in InterfaceBuilder
I have tried 'everything', but it turned out that there are too many difficulties with Core Data on two or more threads and two NSManagedObjectContexts and bindings and entities with relationships and so on. These threads can make CoreData very complicated.
Nevertheless I want to use Core Data and I want it to run in the background for good UI response.
So I wonder, is it possible to completely run everything related to Core Data in one separate thread, which is not the main thread?
I will send everyone 50 bucks, if I finally find a solution that works...

I used two NSManagedObjectContext instances to push some longer lasting data tasks onto another thread with Grand Central Dispatch.
As long as you be careful to merge this context with the one on the main thread used to fetch data then you should be able to get some performance that way.

I am not sure if this can help you, but perhaps you can create a new NSOperationQueue and add whatever core data functions you need to in the background. So in your class have a property that is NSOperationQueue, then in the -viewDidLoad methods, you create new one:
myOperationQueue = [NSOperationQueue new]; Whenever you want to do an operation you can add it to the queue and I believe that it will execute on a separate thread (self.myOperationQueue addOperation: someFunction) and if you want to interact with the main thread, then call assert([NSThread isMainThread]) in the beginning of your method call. I am using Core Data and threading in a different way, but from the second thread I am able to create a temp object that the NSManagedObject is created from.

Related

Outlook Addin: Working with threads

I'm working on an Outlook Addin, and I have to process a large amount of items. This takes quite a lot of time, and I therefore tried to have the processing running in a different thread (using Task.Factory.StartNew). However, that results in Outlook randomly crashing.
I'm using Redemption to work with MAPITable, in order to reduce workload and load only relevant data.
I've tried initializing my RDOSession from both my main thread, and my worker thread.
I've tried getting the MAPIFolders on the main thread, and working with only the MAPITable on the worker thread
Currently, the only thing that works for me is running all my logic on the main thread (in the button click event), however that locks Outlook's user interface for a long period of time, which is unacceptable from a user's point of view.
Does anyone have some pointer on how to work with background threads from within an Outlook Addin?
Having similar code in my project I would suggest the following:
Create new thread using the Thread class and set it apartment to STA.
Loggin to session using "session.Logon("profileName", NoMail: true, NewSession: false);" and not using MAPIOBJECT. I found it has better performance than using MAPIOBJECT, my guess is it still marshal some calls back to the main thread as MAPIOBJECT was created on the main thread.
Use "Marshal.ReleaseComObject" on each and every COM object you use as soon as you are done with them. This is probably what causing the instability as Outlook really doesn't like when it's object are left too long. For example this line of code "var table = rdoFolder.Items.MAPITable;" create two COM objects: RDOItems and MAPITable, both of them must be released so you need to split this line to hold reference to RDOItems object.
Call GC.Collect and Application.DoEvents because if you don't call Marshal.ReleaseComObject on all COM object the finalizer will try to release them and will hang because the COM objects were created on thread that don't pump message loop and it's finalizer method must run on the thread that created them.
If you can, fire a secondary process and do this loop in the separate process. This will make maximum separation between the UI and your background work.
What was the problem using RDO objects in a secondary thread? As long as RDOSession is created on the secondary thread, MAPI should be properly initialized.
Also, TaskFactory uses a thread pool, you'd be better off using an explicit Thread class, o at least make sure that RDOSession is not shared between different threads - MAPI must be initialized on each thread.

How to handle saving on child context but the objected is already deleted in parent context?

I have core data nested contexts setup. Main queue context for UI and saving to SQLite persistent store. Private queue context for syncing data with the web service.
My problem is the syncing process can take a long time and there are the chance that the syncing object is deleted in the Main queue context. When the private queue is saved, it will crash with the "Core Data could not fulfill faulted" exception.
Do you have any suggestion on how to check this issue or the way to configure the context for handle this case?
There is no magic behind nested contexts. They don't solve a lot of problems related to concurrency without additional work. Many people (you seem to be one of those people) expect things to work out of the box which are not supposed to work. Here is a little bit of background information:
If you create a child context using the private queue concurrency type then Core Data will create a queue for this context. To interact with objects registered at this context you have to use either performBlock: or performBlockAndWait:. The most important thing those two methods do is to make sure to invoke the passed block on the queue of the context. Nothing more - nothing less.
Think about this for a moment in the context of a non Core Data based application. If you want to do something in the background you could create a new queue and schedule blocks to do work on that queue in the background. If your job is done you want to communicate the result of the background operations to another layer inside your app logic. What happens when the user deleted the object/data in the meantime which is related to the results from the background operation? Basically the same: A crash.
What you experience is not a Core Data specific problem. It is a problem you have as soon you introduce concurrency. What you need is to think about a policy or some kind of contract between your child and parent contexts. For example, before you delete the object from the root context you should cancel all of the operations/blocks which are running on other queues and wait for the cancellation to finish before you actually delete the object.

What would be a thread-safe way to save a context form Core Data?

I have a NSOperationQueue set to NSOperationQueueDefaultMaxConcurrentOperationCount. It is filled with NSOperation objects (nothing weird so far). I subclassed the NSOperation to do some background tasks.
Download data from the internet.
Parse the data so I can read it.
Create a NSManagedObject:
[NSEntityDescription insertNewObjectForEntityForName:#"Channel" inManagedObjectContext:context];
Save it with the context.
[managedObjectContext save:&error]
I like this all to happen in the background so the UI won't get blocked. I read this article about concurrency with core data, and as far as I understood it. The best way would be to create a new NSManagedObjectContext in every NSOperation, but share the same persistent store coordinator.
That's easily done, however, when it comes to saving the context it says in the documentation it is error prone to do so. So my question is the following:
If I have different operations running in the NSOperationQueue, could those operations interfere with each other while saving the managed object context? Or does it wait to execute the following operation till the saving has been complete?
Can I safely save the context in a NSOperation? Or is it really bad practice?
I hope someone can shine a light on this matter, because I am really stuck at the moment.
What you need to do is the following:
Create a managed object context for each NSOperation. Create this new context on the main method, because this is when it's executing on the right thread.
Assign the context persistent store coordinator.
Create an observer to receive the NSManagedObjectContextDidSaveNotification. This is the only way the main context will know at the time the changes were made on the NSOperation's context. Make sure the merge call is made on the thread/block the merging context lives in. If you are merging with the main thread's context, call the mergeChangesFromContextDidSaveNotification: method on the main thread with the notification from the NSOperation's context.
Also, ask yourself if you really want to have all these operations working concurrently. Per the documentation:
The default maximum number of operations is determined dynamically by the NSOperationQueue object based on current system conditions.
You do not have control over how many NSOperations will be operating at the same time. If this is not what you want, you might be better if you just go with a serial NSOperationQueue (maxConcurrentOperation=1), considering the fact that you are going to be locking the database to do the save, and also because you have networking being done as well.
You can safely save inside the NSOperation's main method, if you take the precautions mentioned above.

Core Data, managed object context, NSArrayController.. straighten me out a bit

So I've got an .xcdatamodel with about a dozen Entities defined and related to each other, with attributes and so on. So far I've been trying this in to a GUI using NSTableViews to display/enter data, and NSArrayControllers that I instantiate for each entity. This is all working great. They all tie in to the App Delegate's Managed Object Context (MOC)
But now I'm trying to programmatically access the data in these arrays, and I'm finding it obtuse to do so. After a lot of reading it looks like what I should REALLY do is go to the MOC to fetch data for a given Entity. I haven't worked through this yet, but ok.
What I don't understand though, is how to use Core Data when I'm NOT entering via NSTableView etc, and NOT using NSArrayControllers. Like if I wanted to totally handle some of my .xcdatamodel Entities in my project's Model packages (that don't touch a GUI). Do I need to still instantiate an NSArrayController so that I can "prepare content" of an Entity and have it be managed and initialized and all that? Or is there another way I can tie in with the MOC directly and add/remove/get the data for a given Entity?
What I'm saying is that I'm really unclear as to how to work with things unless I'm doing simple case View <-> NSArrayController and then Model <-> MOC
You should take a look at NSFetchRequest and the executeFetchRequest:error: method on NSManagedObjectContext.
Accessing the data via the array controller can be tricky. I've found that array controllers are generally designed to be used with UI elements. There are some tricks that the array controller will use to keep the UI snappy. It will fetch items on a background thread, for instance. Much of this can be configured, but you'll be better off accessing the info doing your own fetch.
The NSArrayController and related classes are intended to serve as off the shelf MVC design controllers. As such their only real function is to link the UI to the data model. If you need to deal with the model otherwise, you usually do so programmatically.
To access the model programmatically, you usually start with a fetch request (NSFetchRequest) to find the appropriate instances of certain entities. Then you would walk the entity relationships to find all other instances of the entities related to the fetched entities.
For example: Suppose you had a schedule type app. You entities are days and events. Each day has several events but each event has only one day.
If you want to check the events for a week, you would fetch the day objects whose date attribute feel in the 7 day range. Then you would ask each day object in turn for its related events.
The iOS does not yet support binding so check out the resources for using Core Data there to see how to manage all this manually.

Is NSPasteboard thread-safe?

Is it safe to write data to an NSPasteboard object from a background thread? I can't seem to find a definitive answer anywhere. I think the assumption is that the data will be written to the pasteboard before the drag begins.
Background:
I have an application that is fetching data from Evernote. When the application first loads, it gets the meta data for each note, but not the note content. The note stubs are then listed in an outline view. When the user starts to drag a note, the notes are passed to the background thread that handles getting the note content from Evernote. Having the main thread block until the data is gotten results in a significant delay and a poor user experience, so I have the [outlineView:writeItems:toPasteboard:] function return YES while the background thread processes the data and invokes the main thread to write the data to the pasteboard object. If the note content gets transferred before the user drops the note somewhere, everything works perfectly. If the user drops the note somewhere before the data has been processed... well, everything blocks forever. Is it safe to just have the background thread write the data to the pasteboard?
You can promise the data to the pasteboard without actually having the data yet.
One way is to declare the type of the data on the pasteboard, passing yourself as the pasteboard's owner, and respond to a pasteboard:provideDataForType: message by providing the data (blocking, if necessary, until the data either arrives or fails to arrive). This means that you'll need to remember which objects were copied (by stashing them in an array, for example) so you can extract/generate the data from them when the promise comes due.
The other way, referenced in Harald Scheirich's answer, is to make your model objects conform to the NSPasteboardWriting protocol, ideally in a category (to separate interface-independent logic from Mac-specific logic). This is much cleaner than the old way, but requires Mac OS X 10.6 and later.
With NSPasteboardWriting, you'll implement promises by having the model objects' writingOptionsForType:pasteboard: method return the NSPasteboardWritingPromised option. Their pasteboardPropertyListForType: method will return the data, or at least try to—as before, this method should block until the data either arrives or fails to arrive.
Oh, and to answer the question in the title (“Is NSPasteboard thread-safe?”): There's no specific answer in the Thread Safety Summary, but there is this general statement:
… mutable objects are generally not thread-safe. To use mutable objects in a threaded application, the application must synchronize appropriately.
I would consider an NSPasteboard to be a mutable object, so no.
In practice, this isn't a problem: You typically only work with NSPasteboard in response to an action message (e.g., copy:), a drag, or a service invocation, and those all only happen on the main thread anyway. For them to happen on a secondary thread, you would have to explicitly send such messages yourself from code running on a secondary thread, in which case you are already doing something very wrong.
Conjecture:
I think your problem has nothing to do with threading but the fact that by returning YES you told the system that the data is ready. have you tried moving your data into a custom class supporting NSPasteboardWriting and NSPasteboardReading? this way the accessor to your data can block until the data is ready.
See the Pasteboard Documentation

Resources