CoreData's performBlock: and faulting - ios

I am using the performBlock: and performBlockAndWait: methods to execute fetch requests on my context on a read only database (it is packaged with my app so never written to).
Am I supposed to be wrapping every NSManagedObject accessor inside performBlockAndWait: as well? - that'd be pretty messy. I'm currently getting crashes whenever CoreData is faulting a one-to-many relationship while CoreData's private queue is doing an execute with performBlock:
Something like:
NSManagedObject* alreadyFetchedObject = ...;
NSArray* alreadyFetchedObject.otherObjects; // Crashes here on main thread (no performBlock wrapped around accessing otherObjects)
.
[context performBlockAndWait:^{
// Currently executing here on CoreData's own queue
result = [context executeFetchRequest:fetchRequest error:nil];
}];

Yes, of course. Maybe with the exception for managed objects which have been associated to a context which is executed on the main thread. But for clarity, I would always wrap accesses into performBlock:. Be careful with performBlockAndWait: - this is error prone for dead locks.
Additionally, when you have statements like this:
NSManagedObject* alreadyFetchedObject = ...;
and access alreadyFetchedObject later, you need to ensure that the corresponding managed object context still exists. Thus, always accessing managed objects with performBlock: or performBlockAndWait: reminds you not to accidentally delete the context ;)

Related

Can I use MOC's performBlock: inside an NSOperation subclass?

The docs state that NSManagedObjectContext with NSPrivateQueueConcurrencyType lets the user perform asynchronous code by using performBlock:. But what happens if I want to write an NSOperation subclass for processing managed objects in such a child context/private queue setup?
For example:
// Get managed object IDs from selected objects (defined in one of my own categories).
NSArray * selectedObjIDs = [NSManagedObjectContext IDsWithObjects:self.arrayController.selectedObjects];
NSBlockOperation * operation = [NSBlockOperation blockOperationWithBlock:^
{
NSManagedObjectContext * childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
childContext.parentContext = myMainMOC;
[childContext performBlock:^
{
// Get objects in child context with previously generated managed object IDs (again, from my own category).
NSArray * privateObjects = [childContext objectsWithIDs:selectedObjIDs];
// Do something with the objects.
for( NSManagedObject * object in privateObjects )
{
[object setValue:#"New Title" forKey:#"title"];
}
[childContext save:NULL];
}];
}];
// Execute in our own private NSOperationQueue.
[self.backgroundQueue addOperation:operation];
The code works fine but when setting breakpoints inside both blocks I can see that execution first goes into background thread A (spawned by NSBlockOperation), then into background thread B (dedicated to the child MOC - as expected.
(BTW: I believe I saw an equivalent setup in the sample code of Apple's WWDC session "Advanced NSOperations".)
Question #1: are these two nested dispatches a problem somehow, i.e. in terms of performance? It just doesn't seem right to me - shouldn't the code run in the child MOC's private queue only?
Question #2: imagine I would subclass NSOperation (instead of using NSBlockOperation). Should I override its 'asynchronous' property to return YES to really just use the child MOC's private queue?
Your code isn't exactly wrong but it's more complicated than it needs to be.
When you call performBlock, you are telling the managed object context to execute the block asynchronously on a private queue. That block is already asynchronous with regard to the code that calls performBlock. Wrapping it in an NSOperation is probably safe but also completely unnecessary. As soon as your code calls performBlock, the NSOperation will complete, because of the asynchronous nature of performBlock. In short, the NSOperation is completely unnecessary here.
You could replace performBlock with performBlockAndWait, but that wouldn't make a lot of sense. You'd be forcing the NSOperation to wait, but for no good reason.
Subclassing NSOperation as you mention is also unnecessary unless you have some other asynchronous requirement that you didn't mention. How to configure the NSOperation is irrelevant-- just get rid of it.
Update since in a comment it seems you do have other async requirements...
The code is probably safe depending on what your other operations do.
Since performBlock returns immediately, the next operation in the queue will run in parallel with this one on a different queue. Does that matter? It depends what other operations in the queue are doing. If they depend on this performBlock having finished, that's a problem. You could deal with this using performBlockAndWait to keep the queue serial.

How to "copy" or transfer an NSManagedObject from a context in a private queue to another in the main queue?

I'm requesting data to web services and creating the corresponding NSManagedObject objects in a context with NSPrivateQueueConcurrencyType concurrency type to avoid blocking the main thread. At the end of this process, I need to set the new information I've received to the NSManagedObject objects I have in a context in the main thread (the context that is created in AppDelegate by default).
Which is the correct/appropriate way to do this? I suppose this should be a common scenario, but I don't find any clear example... I've been thinking about some options:
A) To deep-clone somehow the objects in the private context to the main context.
B) To simply do something like this:
[mainContext deleteObject:<oldObjectInMainContext>];
[mainContext insertObject:<newObjectInPrivateContext>];
[mainContext save:nil];
But, since contexts are in different threads, I suppose this would cause problems...
Maybe the appropriate way isn't any of these. I'd appreciate some guidance to handle this scenario.
Thanks in advance.
EDIT: I create the private context this way:
NSManagedObjectContext *privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
and I don't set it as child context of the main one.

Am I approaching concurrency right?

I was wondering if someone could point me in the right direction about whether or not I am using concurrency correctly. I find that currently, the code is very unwieldy and almost unintuitive. For the purposes of my demonstration, please note the CoreData architecture is as described by Marcus Zarra in his article http://martiancraft.com/blog/2015/03/core-data-stack/. Since the operation could take a very long time, I want this entire process to be done in a background thread.
NSManagedContext *context = [[NSManagedContext alloc] initWithConcurrency:NSPrivateConcurrencyQueue];
context.parentContext = [[CoreDataController sharedDispatch] managedObjectContext];
[context performBlock: ^{
NSManagedObject *someManagedObject = [[context executeFetchRequest:request error:nil] firstObject];
NSString *resultFromLongOperation = [self someLongOperation:someManagedObject];
[self doSomething];
BOOL anotherResultFromLongOperation = [self aDifferentLongOperation:someManagedObject];
}];
So from this short example, we can see that I am manipulating the variable someManagedObject, which is of NSManagedObject type and therefore must be used in a performBlock/performBlockAndWait as prescribed by Apple. Should I be passing in the context into someLongOperation and aDifferentLongOperation? If I don't, wouldn't that mean I would have to create another child context and do whatever the function does inside a performBlock/performBlockAndWait and return the result with a __block type? What about in the event if the result of someLongOperation affects the response of aDifferentLongOperation? Am I still structuring my code properly for a situation like this?
Thanks very much in advanced!
I struggled with this same issue not long ago.
I've looked for best practice scenarios and found that:
You indeed, as you stated, shouldn't violate the one context/thread rule set by Apple.
You should normally use completion blocks or notifications with long running operations, whether it's for not making the UI freeze, or just for code clarity.
When you use a completion block, you can create a child context in your long running operation function (someLongOperation) and do whatever you want with it.
If your result is a NSString, then you shouldn't have a problem, because it's not a NSManagedObject and it not bound to any one context.
If you result is a NSManagedObject, then you should pass its NSManagedObjectID to the completion block (with [managedObject objectID] method).
You can use the context's method obtainPermanentIDsForObjects in case you created a new entity and it doesn't have an object ID yet.
Basically, you can use the same context for every long running operation if they all use the same thread. In my case they don't necessarily so I'm creating a new child context for every long running operation just to be sure (creating a new child context is a very fast operation so performance isn't an issue)

Can you use an NSManagedObject outside of it's context's performBlock?

NSManagedObjectContext's have had the performBlock: and performBlockAndWait: methods added to help make concurrency easier. I've been using them -- potentially fairly naively -- and I just realized that there's a question I've never really asked.
If I create an NSManagedObject subclass inside one of the performBlock methods, it's 'home' thread is the thread of it's parent context -- which in the case of the NSPrivateQueueConcurrencyType is probably an independent thread I have no other access to.
So do I need to do a performBlock call just to access the data contained inside my managed objects? Or is there a background magic going on to help protect me in the case of using getters? (Or setters, though that seems like a bad idea...)
NSManagedObject is not supposed to be used outside of its managedObjectContexts thread/queue (sometime it works and some times you crash ==> don't do it).
CoreData does not guarantee safe read access to the object.
To access an object owned by a "private queue" context, always use either [context performBlock:...] or [context performBlockAndWait:...], unless you access its objectID or managedObjectContext properties.
You do need to use performBlock: or performBlockAndWait:, but there's one exception. If you're using NSMainQueueConcurrencyType and you are using the managed object on the main queue, you can access it directly, with no block. This can be a great convenience when you need to update your UI from a managed object, or vice versa.

What is NSManagedObjectContext's performBlock: used for?

In iOS 5, NSManagedObjectContext has a couple of new methods, performBlock: and performBlockAndWait:. What are these methods actually used for? What do they replace in older versions? What kind of blocks are supposed to be passed to them? How do I decide which to use? If anyone has some examples of their use it would be great.
The methods performBlock: and performBlockAndWait: are used to send messages to your NSManagedObjectContext instance if the MOC was initialized using NSPrivateQueueConcurrencyType or NSMainQueueConcurrencyType. If you do anything with one of these context types, such as setting the persistent store or saving changes, you do it in a block.
performBlock: will add the block to the backing queue and schedule it to run on its own thread. The block will return immediately. You might use this for long persist operations to the backing store.
performBlockAndWait: will also add the block to the backing queue and schedule it to run on its own thread. However, the block will not return until the block is finished executing. If you can't move on until you know whether the operation was successful, then this is your choice.
For example:
__block NSError *error = nil;
[context performBlockAndWait:^{
myManagedData.field = #"Hello";
[context save:&error];
}];
if (error) {
// handle the error.
}
Note that because I did a performBlockAndWait:, I can access the error outside the block. performBlock: would require a different approach.
From the iOS 5 core data release notes:
NSManagedObjectContext now provides structured support for concurrent operations. When you create a managed object context using initWithConcurrencyType:, you have three options for its thread (queue) association
Confinement (NSConfinementConcurrencyType).
This is the default. You promise that context will not be used by any thread other than the one on which you created it. (This is exactly the same threading requirement that you've used in previous releases.)
Private queue (NSPrivateQueueConcurrencyType).
The context creates and manages a private queue. Instead of you creating and managing a thread or queue with which a context is associated, here the context owns the queue and manages all the details for you (provided that you use the block-based methods as described below).
Main queue (NSMainQueueConcurrencyType).
The context is associated with the main queue, and as such is tied into the application’s event loop, but it is otherwise similar to a private queue-based context. You use this queue type for contexts linked to controllers and UI objects that are required to be used only on the main thread.
They allow you to access the same managedObjectContext accross threads.
I am not really sure I am correct, but this is how I use it.
You use performBlockAndWait is like "usual". You do not need it if you execute the managedObjectContext only on one thread. If you execute it on many threads then yes you will need performBlock.
So, if you're on main thread, you do not need to do performBlockAndWait for the main managedObjectContext. At least I don't and is doing fine.
However if you access that managedObjectContext on other threads then yes you will need to do performBlockAndWait.
So that's the purpose of performBlock and performBlockAndWait.
Would someone please correct me if I am wrong here. Of course if you access the context only on one thread then you can simply use the default.

Resources