NSOperationQueue waitUntilAllOperationsAreFinished vs. performSelectorOnMainThread - iphone

I have background NSInvocationOperation creating and saving NSArray to the NSManagedObject subclass.
I know that save should happen on main thread, so I use performSelectorOnMainThread for save in the operation.
When user pushes home button on iPhone 3G, app is going to quit. In applicationDidEnterBackground I do [queue waitUntilAllOperationsAreFinished], so that NSInvocationOperation has time to finish.
The problem is, that it waits only for "background part" of the operation - app is shutted down before performSelectorOnMainThread part of the operation is called. This means my NSManagedObject is not saved.
I tried to save object in operation's thread - app is shutted gracefully and changes are saved. But I think this is not good as NSManagedObject is not thread safe. Or is it OK to do this?
It seems like catch 22. I must be missing something - is there any elegant way how to solve this?

You should just do the save operation in the background using a separate context that notifies the main context. This means creating, fetching, and saving managed objects should be done on this separate context and this is documented in the Core Data - Concurrency with Core Data. You should also start a background task to ensure you have enough time to finish saving the data.

Related

Slow closing of UIDocument

I have an application with my UIDocument subclass. But the problem is that from time to time method closeWithCompletionHandler works too long. I even can't wait until the end, it takes too much time. The thing is when I put a breakpoint in - (id)contentsForType:(NSString *)typeName error:(NSError **)outError method, everything works fine. So, the question is what can cause such effect?
Thanks!
Is this code running on the main thread? If that's the case, try running it in a background thread using a dispatch queue.
Note that the main thread powers the UI drawing as well, which is why your app gets stuck when the main thread has too much work to do. Therefore, it is always advisable to put larger operations into a background thread.

Is it safe to reference an NSOperation instance and call -isFinished?

I create an NSOperation every time my app launches or resigns active. I need to queue them with dependencies such that two never execute at the same time, but one after another.
Is it safe to do this?
Hold a strong reference to the NSOperation object in the App Delegate.
When the app resigns active, simply check if hat property is not nil.
If it is not nil, check if the current NSOperation -isFinished.
If it's finished, just add the new one to the queue.
If it's not finished yet, create the new one and set a dependency on the running one, then add it to the queue.
I'm concerned a bit with multithreading issues here. The documentation of the -isFinished or -addDependency: methods doesn't say they should not be called from the main thread. So I guess it is ok to do that.
Edit: The NSOperation performs some file system operations in the background.
If you want to ensure they are not called at the same time, set the maximumConcurrentOperationCount: on your NSOperationQueue to 1.
- (void)setMaxConcurrentOperationCount:(NSInteger)count
This assumes you are putting both of your NSOperations in the same queue.
In response to your other questions. I'm not sure what you are doing - but yes you can hold strong a reference to your NSOperation on the AppDelegate if you want, and you can check isFinished

NSOperation finishes in the background, attempts to notify main thread, view no longer exists. Crash

I have an NSOperation running not in the main thread. It is spawned from a UITableViewController. When the operation is complete, I'd like to reload the tableview since some data has changed. I've set a delegate for the background to notify on completion. When done, I call a wrapper around reloadData specifically on the main thread using performSelectorOnMainThread.
For the most part, this works well, however, there is a non-0 chance that the original (edit)tableViewController (/edit) gets released and I get zombie calls.
So the question is in 2 parts:
Is it possible to have a delegate from the background thread without retaining the object?
Is this just a bad design? Should I be using NSNotifications instead? Would that be the preferred method of notifying in this case?
Thanks in advance.
A delegate should be retained if there is a possibility that it might be released before any operation on the delegate is invoked. You can set up a state in tableViewController to handle the case when the delegate callback is invoked and the tableViewController is not to be used (Basically make the callbacks act as no-op). Once your operation is done, just release the delegate object.
It is not a bad design but you just need to handle these conditions.

Core Data save crashes

I have a UITableView that fetches data from CoreData using FetchedResultsController and it registers for the data update.
On a second thread, I download the data from the server and update the same data (that's used by the UITableView). The update is not complicated and It is just updating a BOOL field of the entity.
When I call the save on Object Context, I get this exception: NSInternalInconsistencyException and the reason is
"Failed to process pending changes before save. The context is still dirty after 100 attempts. ..."
If I do not save right after the update but only at the time when the application is about to terminate, the application runs fine and the UITableView is correctly updated and the data is persisted.
Any pointer on why that might be happening? Am I doing something wrong?
Managed object contexts are not thread safe. Do you have a separate MOC for each thread?
If so, I believe the correct pattern is to register for NSManagedObjectDidSaveNotifications from the background MOC such that you can do a mergeChangesFromContextDidSaveNotification on the main MOC (from the main thread). This will keep your MOCs in sync; it does not happen automatically.

Core data and saving the context in subViewControllers, ok to release?

When updating the managedObjectContext is it ok practice to do the save setup in view controllers that may be released or should the appDelegate handle the saving of the managedObjectContext so that even if the viewController is released the save finishes?
I'm leaning towards the idea of moving the save step into my appDelegate and having viewControllers call [appDelegate saveContext]; when an update is made, though perhaps thats moot since the viewController won't finish releasing until its done saving to CD either way...?
For instance, is there any difference between these two actions, done from a subViewController:
[appDelegate.managedObjectContext save:&error]
and
[appDlegate saveContext]
Where there is a method in appDelegate that runs [managedObjectContext save:&error]
Thanks,
Sam
Guessing that your application is single threaded, you are guaranteed that the save will finish before the view controller is released because the thread will block on the save.
If you are running a multi-threaded application; then
You should not be saving on the background thread, that is dangerous in Core Data.
You should not be accessing the view controller on the background thread because all view related activity should be performed on the main thread.
Therefore, there is no "correct" situation where you would have to worry about a -save: not finishing no matter what object you call it from because it is a blocking call.
If you're just using one managedObjectContext through your app, it's not a bad idea to keep the save functionality in the app delegate, so that regardless of the state of your view controllers through the application's lifecycle, any updates will be saved when the app terminates.
That being said, I've found it useful to add additional save points, sometimes in view controllers, that will save the database after doing some significant updates. This way I've got my data saved even if the app crashes or the final save operation is otherwise prevented from completing.
I'm a relative newbie to Core Data so please inform me if this is bad practice.