Pregunta

Estoy usando Grand Central Dispatch (GCD) en mi solicitud para hacer algo de trabajo pesado. La aplicación utiliza los datos básicos para fines de almacenamiento de datos. Aquí está mi escenario (junto con la pregunta relevante):

dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_queue_t request_queue = dispatch_queue_create("com.app.request", NULL);

dispatch_async(request_queue, ^{
    MyNSManagedObject *mObject = [self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];

    // … 
    // <heavy lifting>
    // … 

    // … 
    // <update mObject>
    // … 

    [self saveManagedObjectContext];
});     

Como resultado de [self saveManagedObjectContext], métodos de delegado fetchResultsController se llama automáticamente. En consecuencia, las patadas lógicos UI updation.

Ahora mi pregunta es, ¿tengo que usar para main_queue -saveManagedObjectContext? ¿Debo realizar todas las operaciones en mi NSManagedObject en main_queue? Algunas de las operaciones que actualizan la NSManagedObject podría tomar de 2-3 segundos. Por favor avise.

¿Fue útil?

Solución

Como usted probablemente sabe o ha notado debe llevar a cabo operaciones de interfaz de usuario en el hilo principal. Como lo mencionas es cuando se guarda la actualización de la interfaz de usuario se lleva a cabo. Puede resolver este anidando una llamada a dispatch_sync en el hilo principal.

dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_queue_t request_queue = dispatch_queue_create("com.app.request", NULL);

__block __typeof__(self) blockSelf = self;

dispatch_async(request_queue, ^{
    MyNSManagedObject *mObject = [blockSelf.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];

    // update and heavy lifting...

    dispatch_sync(main_queue, ^{
      [blockSelf saveManagedObjectContext];
    });
});     

El uso de blockSelf es para evitar la creación accidentalmente ciclos de referencia. ( bloques prácticos )

Otros consejos

Hay una regla de oro cuando se trata de datos básicos - uno Managed Object Context por hilo. contextos de objetos gestionados no están hilo tan seguro si se está haciendo el trabajo en una tarea de fondo se utiliza ya sea el hilo conductor para evitar el roscado conflictos con las operaciones de la interfaz de usuario, o crear un nuevo contexto para hacer el trabajo. Si el trabajo va a tomar unos segundos, entonces debería hacer esto último para detener la interfaz de usuario se bloqueen.

Para hacer esto se crea un nuevo contexto y darle el mismo almacén persistente como su marco principal:

NSManagedObjectContext *backgroundContext = [[[NSManagedObjectContext alloc] init] autorelease];
[backgroundContext setPersistentStoreCoordinator:[mainContext persistentStoreCoordinator]];

Haz lo operaciones que hay que hacer, cuando guarde este nuevo contexto que necesita para manejar el proceso de almacenamiento de notificación y combinar los cambios en su marco principal con el mensaje mergeChangesFromContextDidSaveNotification:. El código debe ser algo como esto:

/* Save notification handler for the background context */
- (void)backgroundContextDidSave:(NSNotification *)notification {
    /* Make sure we're on the main thread when updating the main context */
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(backgroundContextDidSave:)
                               withObject:notification
                            waitUntilDone:NO];
        return;
    }

    /* merge in the changes to the main context */
    [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}

/* ... */

/* Save the background context and handle the save notification */
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(backgroundContextDidSave:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:backgroundContext];

[backgroundContext save:NULL];

[[NSNotificationCenter defaultCenter] removeObserver:self
                                                name:NSManagedObjectContextDidSaveNotification
                                              object:syncContext];

Cuidado de la Guardar aviso y fusión es importante de lo contrario su principal UI / contexto no verá los cambios realizados. Mediante la fusión, su principal fetchResultsController etc. recibirá los eventos de cambio y actualizar la interfaz de usuario como era de esperar.

Otra cosa importante a destacar es que las instancias NSManagedObject sólo se pueden utilizar en el contexto de que eran obtienen de. Si su operación necesita una referencia a un objeto a continuación, usted tiene que pasar objectID del objeto de la operación y vuelva a recuperar una instancia NSManagedObject del nuevo contexto utilizando existingObjectWithID:. Así que algo como:

/* This can only be used in operations on the main context */
MyNSManagedObject *objectInMainContext =
    [self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];

/* This can now be used in your background context */
MyNSManagedObject *objectInBackgroundContext =
    (MyNSManagedObject *) [backgroundContext existingObjectWithID:[objectInMainContext objectID]];

Desde la base de datos requiere un contexto de objetos administrados por hilo, una posible solución sería la de realizar un seguimiento de un contexto por hilo en un gestor global, a continuación, realizar un seguimiento de las notificaciones Guardar y propagar a todas las discusiones:

Suponiendo:

@property (nonatomic, strong) NSDictionary* threadsDictionary;

Aquí es cómo conseguir el objeto gestionado (por hilo):

- (NSManagedObjectContext *) managedObjectContextForThread {

// Per thread, give one back
NSString* threadName = [NSString stringWithFormat:@"%d",[NSThread currentThread].hash];

NSManagedObjectContext * existingContext = [self.threadsDictionary objectForKey:threadName];
if (existingContext==nil){
    existingContext = [[NSManagedObjectContext alloc] init];
    [existingContext setPersistentStoreCoordinator: [self persistentStoreCoordinator]];
    [self.threadsDictionary setValue:existingContext forKey:threadName];
}

return existingContext;

}

En algún momento en el método init de su administrador mundial (I utiliza un conjunto unitario):

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundContextDidSave:)                                                    name:NSManagedObjectContextDidSaveNotification                                                   object:nil];

Luego de recibir notificaciones guardar y propagar a todos los demás objetos de contexto administrado:

- (void)backgroundContextDidSave:(NSNotification *)notification {
    /* Make sure we're on the main thread when updating the main context */
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(backgroundContextDidSave:)
                               withObject:notification
                            waitUntilDone:NO];
        return;
    }

    /* merge in the changes to the main context */
    for (NSManagedObjectContext* context in [self.threadsDictionary allValues]){
            [context mergeChangesFromContextDidSaveNotification:notification];
    }
}

(algunos otros métodos fueron retirados para mayor claridad)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top