2012-04-12 11 views
5

que estoy haciendo operaciones en una cola GCD despacho en un NSManagedObjectContext define así:NSSortDescriptor de NSFetchRequest no funcionar después de guardar contexto

- (NSManagedObjectContext *)backgroundContext 
{ 
    if (backgroundContext == nil) { 
     self.backgroundContext = [NSManagedObjectContext MR_contextThatNotifiesDefaultContextOnMainThread]; 
    } 
    return backgroundContext; 
} 

MR_contextThatNotifiesDefaultContextOnMainThread es un método de MagicalRecord:

NSManagedObjectContext *context = [[self alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
[context setParentContext:[NSManagedObjectContext MR_defaultContext]]; 
return context; 

Después buscando mis objetos y dándoles la posición de cola correcta, los registro y el orden es correcto. Sin embargo, el segundo registro parece ser completamente aleatorio, el descriptor de clasificación claramente no está funcionando.

He reducido el problema a [self.backgroundContext save:&error]. Después de guardar el contexto de fondo, las descripciones de clasificación están rotas.

dispatch_group_async(backgroundGroup, backgroundQueue, ^{ 
    // ... 

    for (FooObject *obj in fetchedObjects) { 
     // ... 
     obj.queuePosition = [NSNumber numberWithInteger:newQueuePosition++]; 
    } 

    NSFetchRequest *f = [NSFetchRequest fetchRequestWithEntityName:[FooObject entityName]]; 
    f.predicate = [NSPredicate predicateWithFormat:@"queuePosition > 0"]; 
    f.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"queuePosition" ascending:YES]]; 
    NSArray *queuedObjects = [self.backgroundContext executeFetchRequest:f error:nil]; 
    for (FooObject *obj in queuedObjects) { 
     DLog(@"%@ %@", obj.queuePosition, obj.title); 
    } 

    if ([self.backgroundContext hasChanges]) { 
     DLog(@"Changes"); 
     NSError *error = nil; 
     if ([self.backgroundContext save:&error] == NO) { 
      DLog(@"Error: %@", error); 
     } 
    } 

    queuedObjects = [self.backgroundContext executeFetchRequest:f error:nil]; 
    for (FooObject *obj in queuedObjects) { 
     DLog(@"%@ %@", obj.queuePosition, obj.title); 
    } 

}); 

No tengo idea de por qué el descriptor de ordenación no funciona, ¿algún experto de Core Data quiere ayudar?

Actualización:

El problema no se produce en IOS 4. Creo que la razón es en algún lugar de la diferencia entre el aislamiento del hilo y modos de cola privada. MagicalRecord utiliza automáticamente el nuevo patrón de concurrencia que parece comportarse de manera diferente.

Actualización 2:

El problema se ha resuelto mediante la adición de un ahorro del contexto de fondo:

if ([[NSManagedObjectContext MR_contextForCurrentThread] hasChanges]) { 
    DLog(@"Changes"); 
    NSError *error = nil; 
    if ([[NSManagedObjectContext MR_contextForCurrentThread] save:&error] == NO) { 
     DLog(@"Error: %@", error); 
    } else { 
     NSManagedObjectContext *parent = [NSManagedObjectContext MR_contextForCurrentThread].parentContext; 
     [parent performBlockAndWait:^{ 
      NSError *error = nil; 
      if ([parent save:&error] == NO) { 
       DLog(@"Error saving parent context: %@", error); 
      } 
     }]; 
    } 
} 

Actualización 3:

MagicalRecord ofrece un método para guardar de forma recursiva un contexto, ahora mi código se ve así:

if ([[NSManagedObjectContext MR_contextForCurrentThread] hasChanges]) { 
    DLog(@"Changes"); 
    [[NSManagedObjectContext MR_contextForCurrentThread] MR_saveWithErrorHandler:^(NSError *error) { 
     DLog(@"Error saving context: %@", error); 
    }]; 
} 

La culpa es mía por no usarlo en el primer lugar ...

Sin embargo, no sé por qué que esto ayude y le encantaría tener una explicación.

Respuesta

2

Voy a tratar de comentar, que no escribo MagicalRecord.

Por lo tanto, en iOS5, MagicalRecord está configurado para intentar utilizar el nuevo método de cola privada de múltiples contextos de objetos gestionados. Esto significa que un guardado en el contexto secundario solo empuja las salvaciones al padre. Solo cuando se guarda un padre sin más padres, el ahorro persiste en su tienda. Esto es probablemente lo que estaba sucediendo en su versión de MagicalRecord.

MagicalRecord ha intentado manejar esto para usted en las versiones posteriores. Es decir, intentaría elegir entre el modo de cola privada y el modo de aislamiento de subprocesos. Como descubriste, eso no funciona demasiado bien.La única forma verdaderamente compatible de escribir código (sin reglas complejas de preprocesador, etc.) para iOS4 e iOS5 es utilizar el modo clásico de aislamiento de subprocesos. MagicalRecord de la etiqueta 1.8.3 lo admite, y debería funcionar para ambos. Desde 2.0, serán solo las colas privadas de aquí en adelante.

Y, si observa el método MR_save, verá que también está realizando la comprobación de cambios por usted (que también puede ser innecesario ya que Core Data Internals también puede manejar eso). De todos modos, menos código debe escribir y mantener ...

+1

Gracias por su contribución, experimenté un poco más con el aislamiento de subprocesos solamente, pero preferiría utilizar colas privadas en iOS 5. Durante las pruebas creo que encontré un error. El contexto save del padre se invoca en el hilo del contexto secundario, lo que genera problemas al guardar recursivamente desde un contexto GCD. He enviado una solicitud de extracción: https://github.com/magicalpanda/MagicalRecord/pull/159 – tim

-2

Dado que CoreData no es una estructura segura de subprocesos y para cada subproceso (cola de operaciones), los datos centrales utilizan contextos de diferencia. Por favor, consulte la siguiente escritura excelente

http://www.duckrowing.com/2010/03/11/using-core-data-on-multiple-threads/

+0

10 backgroundContext se inicializa desde el bloque GCD y ningún ManagedObject o ManagedObjectContext cruza los límites del hilo, por lo que no creo que ese sea mi problema aquí. – tim

1

La razón subyacente real por la cual su configuración original no funcionó es un error de Apple cuando se extrae de un contexto secundario con descripciones de ordenación cuando el contexto primario aún no está salvo para almacenar:

NSSortdescriptor ineffective on fetch result from NSManagedContext

Si hay alguna manera se puede evitar contextos anidados, hacer evitarlos, ya que todavía son extremadamente buggy y es probable que decepcionado con las supuestas mejoras de rendimiento, cf. también: http://wbyoung.tumblr.com/post/27851725562/core-data-growing-pains

Cuestiones relacionadas