2012-04-12 5 views
5

sto facendo operazioni in una coda GCD messaggio concernente un NSManagedObjectContext definita in questo modo:NSSortDescriptor di NSFetchRequest non funziona dopo contesto Salva

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

MR_contextThatNotifiesDefaultContextOnMainThread è un metodo da MagicalRecord:

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

Dopo recuperando i miei oggetti e dando loro la corretta posizione in coda li registro e l'ordine è corretto. Tuttavia, il secondo registro sembra essere completamente casuale, il descrittore di ordinamento chiaramente non funziona.

Ho ristretto il problema a [self.backgroundContext save:&error]. Dopo aver salvato il contesto, i descrittori di ordinamento vengono interrotti.

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); 
    } 

}); 

ho idea del perché il descrittore di tipo non funziona, gli esperti Core Data vogliono dare una mano?

Aggiornamento:

Il problema non si verifica su iOS 4. Penso che il motivo è da qualche parte nella differenza tra filo isolamento e le modalità di coda privati. MagicalRecord utilizza automaticamente il nuovo modello di concorrenza che sembra comportarsi diversamente.

Aggiornamento 2:

Il problema è stato risolto con l'aggiunta di un salvataggio del contesto di 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); 
      } 
     }]; 
    } 
} 

Update 3:

MagicalRecord offre un metodo per salvare in modo ricorsivo un contesto, ora il mio codice assomiglia a questo:

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

Vergogna su di me per non usarlo in primo luogo ...

Tuttavia, non so il motivo per cui questo aiuta e mi piacerebbe avere una spiegazione.

risposta

2

cercherò di commentare, da quando ho scritto MagicalRecord.

Quindi, su iOS5, MagicalRecord è impostato per provare a utilizzare il nuovo metodo di coda privata di più contesti di oggetti gestiti. Ciò significa che un salvataggio nel contesto secondario spinge solo i salvataggi fino al genitore. Solo quando un genitore che non ha più genitori salva, il salvataggio rimane nel suo archivio. Questo è probabilmente ciò che stava accadendo nella tua versione di MagicalRecord.

MagicalRecord ha provato a gestirlo per voi nelle versioni successive. Cioè, proverebbe a scegliere tra la modalità di coda privata e la modalità di isolamento del thread. Come hai scoperto, non funziona troppo bene.L'unico modo veramente compatibile per scrivere codice (senza regole complesse per il preprocessore, ecc.) Per iOS4 e iOS5 consiste nell'usare la classica modalità di isolamento del thread. MagicalRecord dal tag 1.8.3 supporta questo e dovrebbe funzionare per entrambi. Da 2.0, saranno solo le code private da qui in avanti.

E, se si guarda nel metodo MR_save, vedrete che sta anche eseguendo il controllo hasChanges per voi (che potrebbe anche non essere necessario dal momento che il Anche i dati interni dei Core Data possono gestirli). In ogni caso, meno codice dovresti scrivere e mantenere ...

+1

Grazie per l'input, ho sperimentato un po 'di più solo con l'isolamento del thread ma preferirei usare code private su iOS 5. Durante i test penso di aver trovato un bug. Il salvataggio del contesto genitore viene chiamato sul thread del contesto figlio, il che porta a problemi durante il salvataggio in modo ricorsivo da un contesto GCD. Ho inviato una richiesta di pull: https://github.com/magicalpanda/MagicalRecord/pull/159 – tim

-2

Poiché CoreData non è un framework di thread sicuro e per ogni thread (coda di operazioni), i dati di base utilizzano contesti di differenza. Si prega di fare riferimento alla seguente scrittura eccellente

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

+0

Il backgroundContext viene inizializzato dal blocco GCD e nessun ManagedObject o ManagedObjectContext attraversa i bordi del thread quindi non penso che sia il mio problema qui. – tim

1

L'effettivo motivo sottostante per cui la tua configurazione originale non ha funzionato è un bug di Apple quando si preleva da un contesto figlio con descrittori di ordinamento quando il contesto genitore non è ancora salvato per memorizzare:

NSSortdescriptor ineffective on fetch result from NSManagedContext

Se non v'è alcun modo si può evitare contesti nidificati, fare evitarli in quanto sono ancora estremamente buggy e si sarà probabilmente delusi con l'incremento delle prestazioni supposte, cf. anche: http://wbyoung.tumblr.com/post/27851725562/core-data-growing-pains

Problemi correlati