¿La mejor práctica para una operación en primer plano de ejecución prolongada que usa Core Data?

I have an app that imports a potentially large amount of data from the web after the user explicitly presses a Sync button, and stores that data using Core Data. Since I want to show feedback and I don't want the user interacting with the rest of the app while this happens, pressing the Sync button brings up a Modal dialog. Since I want the operation to happen immediately, the operation executes in the viewDidAppear method. I'm sure this is frowned upon.

There are a bunch of problems with the approach right now:

  • Everything happens in the main thread. The user kind of gets feedback because there is an activity indicator that continues to animate, but there's no way to indicate progress or show intermediate messages. This is not the right way to do things.
    • But, I am told that when using Core Data, everything has to use the main thread, so breaking off the work into another thread does not seem like it will be straightforward.
  • If the app enters the background state (user hits Home button or iPad falls sleep), it's game over - the operation dies. It's clear to me from the documentation why this is the case.
    • I know there are "I'm about to enter the background" events that you can handle, but it's not as though I can move execution of code from one place to another in the middle of a file download. Whatever solution I use has to be a continuous action that executes in the same way both before and after the transitions to/from the background.
    • I want the operation to execute in the foreground as far as the user is concerned. It does not make sense for them to interact with other parts of the app while this operation is taking place.

I am reading the Apple documentation on this, but I'm asking this in hopes of finding more concise guidance on this particular combination of needs. Thanks.

preguntado el 03 de mayo de 12 a las 20:05

Your statement about Core Data requiring the main thread is not true. Please read the concurrency section of: desarrollador.apple.com/library/ios/#documentation/Cocoa/Conceptual/… -

@T Reddy You're right. My statement is only true if I were to try to pass the main thread's managed object context to the worker thread. If I create a new managed object context in the worker thread, that is supported (I just have to make sure I do something to make the main thread's manage object context aware of the changes the worker's context made). -

Not just the context, but any managed object cannot cross thread boundaries. I have used this managed object wrapper to facilitate the threading requirements as outlined by the Core Data documentation: github.com/chriscdn/RHManagedObject -

1 Respuestas

You really should not freeze the main thread. You can still "prohibit" certain UI actions.

Create a separate context, as a child, and do all your work in there. When done (or at certain intervals), save the context to the main context, and notify the main thread to do some UI update interaction... maybe a progress bar or something...

NSManagedContext *backgroundContext = [NSManagedContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backgroudContext.parentContext = [self mainManagedObjectContext];
[backgroundContext performBlock:^{
    // This block is running in a background thread.
    // Go get your data from the web

    // Call this to push data to the main MOC (either at end, or at intervals)
    [backgroundContext save:&error];

    // When you want to do something on the main thread...
    dispatch_async(dispatch_get_main_queue(), ^{
        // This block is running on the main queue... I can do anything with the UI...

Couple of things to note... your mainMOC needs to be private or main queue concurrency type. If you are using the Core Data template, where it is in the app delegate, just change the alloc/init to initWithConcurrencyType:NSMainQueueConcurrencyType.

I would, however, suggest using the canonical main/parent relationship. Create a private MOC, assign it to the persistent store, then create a main MOC, set its parent to be that private MOC. Now you are ready to handle any I/O with background operations, without blocking your UI.

Still, when loading from the web, use the pattern above: create a child MOC, then load objects into the main MOC.

Note, that the data is not saved to disk until the "root" MOC calls save.

contestado el 08 de mayo de 12 a las 16:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.