What does NSManagedContext reset do? - objective-c

Here's my scenario: I have a thread running heavy calculations, saving the results via core data at the end. I have the thread set up with it's own autorelease pool and it's own NSManagedContext created within the thread. The user can change the inputs to the calculation on the main thread, in which case the calculation thread is terminated (via regular checks of a NSLocked variable) and relaunched with the new inputs. Everything is working fine here.
For performance reasons, the calculation thread doesn't have an undo manager and there is only one context save at the very end. If a termination command is detected I don't want to save the results. Right now I'm just skipping the context save and releasing it, which seems to work fine.
I noticed, however, that there's a reset method for NSManagedContext. Apple's documentation on this method isn't very helpful to me. It simply states that it returns the receiver's contents to it's base state and that all the receiver's managed objects are "forgotten".
What does that mean? Is it the equivalent to reverting to the last saved version? Is an undo manager required for proper operation of this method? Any reason I should use this method instead of what I'm doing now?

It sounds like you are using the context to cache changes independent of the context on the main thread, and if you don't want those changes to be recorded, you just throw them out by deleting the "local" context. This is good enough for the scenario you are describing. -reset might be useful if you didn't want to relaunch the background thread, but just start over using the same thread (and context), but with new inputs. Since you launch a new thread (thus creating a new NSManagedObjectContext on it), -reset is probably not very useful for you in this scenario. You already pretty much doing it as Apple recommends in several of their sample codes.

Related

What high level synchronisation construct should be used for thread safe single shot method?

I have a situation where a session of background processing can finish by timing out, user asynchronously cancelling or the session completing. Any of those completion events can run a single shot completion method. The completion method must only be run once. Assume that the session is an instance of an object so any synchronisation must use instance constructs.
Currently I'm using an Atomic Compare and Swap operation on a completion state variable so that each event can test and set the completion state when it runs. The first completion event to fire gets to set the completed state and run the single shot method and the remaining events fail. This works nicely.
However I can't help feeling that I should be able to do this in a higher level way. I tried using a Lock object (NSLock as I'm writing this with Cocoa) but then got a warning that I was releasing a lock that was still in the locked state. This is what I want of course. The lock gets locked once and never unlocked but I was afraid that system resources representing the lock might get leaked.
Anyway, I'm just interested as to whether anyone knows of a more high level way to achieve a single shot method like this.
sample code for any of the completion events:
if(OSAtomicCompareAndSwapInt(0, 1, &completed))
{
self.completionCallback();
}
Doing a CAS is almost certainly the right thing to do. Locks are not designed for what you need, they are likely to be much more expensive and are semantically a poor match anyway -- the completion is not "locked". It is "done". A boolean flag is the right representation, and doing a CAS ensures that it is manipulated safely in concurrent scenarios. In C++, I'd use std::atomic_flag for this, maybe check whether Cocoa has anything similar (this just wraps the CAS in a nicer interface, so that you never accidentally use a non-CAS test on the variable, which would be racy).
(edit: in pthreads, there's a function called pthread_once which does what you want, but I wouldn't know about Cocoa; the pthread_once interface is quite unwieldy anyway, in my opinion...)

What's the point of using performBlockAndWait in new iOS5 NSManagedObjectContext?

I am modifying my program to use the new iOS5 style.
So I simply use this code:
NSManagedObjectContext *threadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
threadContext.parentContext = [self managedObjectContextMainThread];
//threadContext.persistentStoreCoordinator= [self persistentStoreCoordinator]; //moc.persistentStoreCoordinator;// [moc persistentStoreCoordinator];
My new background ManagedObjectContext doesn't have a persistentStore but have parent store instead.\
After that I suppose I am supposed to add
performBlockAndWait on all operation where I use all operation that use the new MOC.
I don't use that and doing just fine at least so far
performBlockAndWait is done by executing the block at the same thread and wait till it's complete.
What's the difference between that and just type the code like usual?
I mean there has to be some used, but I am totally missing here.
I can understand performBlock. That'll be like executing something in back ground. Even then it's superseded with Global Central Dyspatch.
Yes there is this new thing called Queue. Okay, if we do something on the same thread, of course everything is done consecutively. Duh.... So why the queue?
Anyone care to explain?
It is possible that the thread that execute the block is not the same with the thread that call performBlockAndWait.
For example, some core data object may only be able to be executed at main thread.
Hence, the performBlockAndWait would do it on a main thread (different thread) and block the current thread.
Also it's saver. Core data would lock things up appropriately preventing collision. If you have several thread accessing the same managed object context, you need to pull this up.
The reason for performBlockAndWait: is it will get and hold the concurrency lock to access Core Data. You can consider it a modernization of the lock/unlock approach, but that's undocumented implementation detail.
If you just execute the code directly, it won't do proper concurrency locking. This is interesting for a number of reasons:
Requests to Core Data won't be properly serialized. That is, if you performBlock: (no wait) the code could end up executing at the same time as other Core Data code, which would probably cause a problem in the coordinator or persistent store.
It… well, I actually don't think it should work. It seems to most of the time in practice, but you're running Core Data without necessary locks. Pretty sure you're into undocumented behaviour here at a minimum.
So:
performBlockAndWait: sets up an environment where your block can access Core Data via the context and waits for the block to complete.
The documentation says nothing about the thread. It's not actually documented as running on the current thread.
Even if it doesn't now, it could be changed in the future to go to secondary threads in at least some circumstances.
Read the parent point again: That's what you're supposed to rely on. The rest is just details.
performBlock: sets up an environment where your block and access Core Data via the context and does not wait for the block to complete.
The documentation says nothing about the thread. It's not actually documented as running on a different thread.
Although unlikely, a future version of the OS could decide to run the block on the current thread at a later time.
Again, the parent point is what you're to rely on. The rest is undocumented details.
I hope that helps. Basically, you're supposed to play dumber than you are when touching these calls. Let the OS do the right thing, just try not to make assumptions about what it's doing. :)
The NSPrivateQueueConcurrencyType constant sets up too many expectations for how this works.

Should my block based methods return on the main thread or not when creating an iOS cloud integration framework?

I am in the middle of creating a cloud integration framework for iOS. We allow you to save, query, count and remove with synchronous and asynchronous with selector/callback and block implementations. What is the correct practice? Running the completion blocks on the main thread or a background thread?
For simple cases, I just parameterize it and do all the work i can on secondary threads:
By default, callbacks will be made on any thread (where it is most efficient and direct - typically once the operation has completed). This is the default because messaging via main can be quite costly.
The client may optionally specify that the message must be made on the main thread. This way, it requires one line or argument. If safety is more important than efficiency, then you may want to invert the default value.
You could also attempt to batch and coalesce some messages, or simply use a timer on the main run loop to vend.
Consider both joined and detached models for some of your work.
If you can reduce the task to a result (remove the capability for incremental updates, if not needed), then you can simply run the task, do the work, and provide the result (or error) when complete.
Apple's NSURLConnection class calls back to its delegate methods on the thread from which it was initiated, while doing its work on a background thread. That seems like a sensible procedure. It's likely that a user of your framework will not enjoy having to worry about thread safety when writing a simple callback block, as they would if you created a new thread to run it on.
The two sides of the coin: If the callback touches the GUI, it has to be run on the main thread. On the other hand, if it doesn't, and is going to do a lot of work, running it on the main thread will block the GUI, causing frustration for the end user.
It's probably best to put the callback on a known, documented thread, and let the app programmer make the determination of the effect on the GUI.

Undoing Core Data insertions that are performed off the main thread

I'm working on some code that uses an NSOperation to import data. I'd like for the user to be able to undo the NSManagedObject instances that are created during the import operation.
From what I can tell, it's impossible to use the NSManagedObjectContext -undoManager for any operations that are performed off of the main thread. From the Core Data Programming Guide section on Use Thread Confinement to Support Concurrency, we have these two conditions:
Only objectID should be passed
between managed object contexts (on
separate threads)
Managed objects
must be saved in a context before
the objectID can be used.
This makes sense since the managed objects need to be moved from private storage (NSManagedObjectContext) to public storage (NSPersistentStore) before they can be shared.
Unfortunately, the -save: message also causes any managed objects in the undo stack to be removed. From the Memory Management Using Core Data section of the same guide:
Managed objects that have pending
changes (insertions, deletions, or
updates) are retained by their context
until their context is sent a save:,
reset , rollback, or dealloc message,
or the appropriate number of undos to
undo the change.
I've tried several things to work around this limitation, and everything eventually leads back to bulk of the work happening on the main thread (and spinning beach balls.) Any clues to getting undo working with objects created off the main thread would be very much appreciated.
--
An enhancement Radar has been submitted: rdar://problem/8977725
This answer will probably be a bit of a back and forth. If I understand the issue correctly, you are doing an import but when the import is done you want the user to be able to select what gets saved from the import?
If that is not correct, please fix my assumptions and I will update this answer.
If it is correct then what you can do is:
Change your background object creation to
NSEntityDescription *myEntity = ... //Entity from your context
[[NSManagedObject alloc] initWithEntity:myEntity
insertIntoManagedObjectContext:nil];
Store these entities in an array.
Pass the entities back to your main thread as needed.
Release on any objects you don't want to keep
Call [myMainContext insertObject:managedObject] on any you want to keep.
Perform a save on the NSManagedObjectContext.
Since these entities are not part of a NSManagedObjectContext yet they only exist in memory and should be thread safe since they are not yet tied down to a NSManagedObjectContext.
This is of course theoretical and will require testing. However it should accomplish your goal.
Not an expert, but I think what you're going to need to do is create a second context to perform the operations, then merge the two contexts together. You should be able to manage the merge as an undo step. Note, this only works if you're treating the entire set of operations as one undo step, as far as the user is concerned.
Suppose that you use a separate context for the background thread, and once it's done, push a [[backgroundContext undoManager] undo] onto the foreground thread's undo stack? I've never tried anything like that, but off the top of my head I can't think of a reason it shouldn't work.
One option may be to make your import thread persistent. Even when the thread is finished importing, it goes into an idle loop state. This way your threaded ManagedObjectContext is persisted in the proper thread. Then when the user wishes to undo a change, send a message to the thread to use the undomanager.
It's incredibly likely you've considered this and you're likely only looking for a solution using the existing undoManager, but just in case:
Since you're inserting objects and not updating existing ones, you do have the power to tag them with a transaction id as each batch is imported, deleting them in a background thread in the case of an undo. A simple incremented NSNumber is sufficient for the tag.
Inelegant, but workable.

Crashes in Core Data's Inferred Mapping Model Creation (Lightweight Migration). Threading Issue?

I'm getting random crashes when creating an inferred mapping model (with Core Data's lightweight migration) within my application. By the way, I have to do it programmatically in my application while it is running.
This is how I create this model (after I have made proper currentModel and newModel objects, of course):
NSMappingModel *mappingModel = [NSMappingModel inferredMappingModelForSourceModel:currentModel destinationModel:newModel error:&error];
The problem is this: This method is crashing randomly. When it works, it works just fine without issues. But when it crashes, it crashes my application (instead of returning nil to signify that the method failed, as it should). By randomly, I mean that sometimes it happens and sometimes not. It is unpredictable.
Now, here is the deal: I'm running this method in another thread. More precisely, it is located inside a block that is passed via GCD to run on the global main queue. I need to do this for my UI to appear crisp to the user, i.e. so that I can display a progress indicator while the work is underway.
The strange thing seems to be that if I remove the GCD stuff and just let it run on the main thread, it seems to be working fine and never crashing. Thus, could it be because I'm running this on a different thread that this is crashing?
I somehow find that weird because I don't believe I'm breaking any Core Data rules regarding multi-threading. In particular, I'm not passing any managed objects around, and whenever I need access to the MOC, I create a new MOC, i.e. I'm not relying on any MOC (or for that matter: anything) that has been created earlier on the main thread. Besides the little MOC stuff that occurs, occurs after the mapping model creation method, i.e. after the point at which the app crashes, so it can't possibly be a cause of the crashes under consideration here.
All I'm doing is taking two MOMs and asking for a mapping model between them. That can't be wrong even under threading, now can it?
Any ideas on what could be going on?
First, what is the crash?
Second, Core Data generally is a single threaded API. There are things you can do in multiple threads but creating a NSMappingModel is most likely not one of them. Why must you create the mapping model dynamically? If the MOMs are a known quantity then the mapping can be a known quantity as well.
update
First, the threading issue. Core Data is meant to be single threaded. However, the NSManagedObjectContext knows how to lock the NSPersistentStoreCoordinator correctly therefore you can have one NSManagedObjectContext per thread because they know how to lock correctly. However this is not the case when you are working with and creating a mapping model.
However, the error you provided is not a Core Data error per se. That error indicates that somewhere in your code you are trying to stick a nil into a set. Without seeing the code that is generating the mapping model though it is difficult to guess as to exactly where.
Have you put a breakpoint at objc_throw_exception and see what line in your code is causing this crash? If it is in something non-obvious then I would suggest there is some point in your building of the mapping model that is giving Core Data an unexpected nil.
One thing you can try is locking the NSPersistentStore and/or the NSManagedObjectContext yourself to see if that resolves the crash. However I suspect when you do that you are going to again deal with performance issues.
I ended up giving up on this problem altogether and just created the damned mapping models myself.