Ho un problema molto strano con relazioni inverse in Core Data e sono riuscito a ridurre il mio problema a un esempio minimo, a partire da un nuovo progetto in xcode basato sul modello di finestra con supporto per Core Data (cioè, c'è molto poco lì).Relazione inversa non impostata (nel gestore KVO)
Supponiamo di disporre di un modello Core Data con tre entità: Dipartimento, Dipendente e Repository (una sorta di entità che rappresenta alcune statistiche sul dipartimento). Per semplicità, abbiamo solo relazioni uno-a-uno:
DepartmentSummary Department Employee
---------------------------------------------------------
employee <----> department
department <----> summary
Questo è tutto ciò che c'è nel modello. In application:didFinishLaunchingWithOptions:
creiamo un impiegato e di un reparto e impostare KVO:
NSManagedObject* employee =
[NSEntityDescription
insertNewObjectForEntityForName:@"Employee"
inManagedObjectContext:[self managedObjectContext]];
[employee addObserver:self forKeyPath:@"department" options:0 context:nil];
NSManagedObject* department =
[NSEntityDescription
insertNewObjectForEntityForName:@"Department"
inManagedObjectContext:[self managedObjectContext]];
[department setValue:employee forKey:@"employee"];
Lo scopo del gestore KVO è quello di creare una sintesi per il reparto non appena reparto del dipendente è impostato:
- (void) observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
[self createSummary:object];
}
createSummary
è semplice: si crea un nuovo oggetto sintesi e associa con il reparto, e poi controlla che il rapporto inverso dal dipartimento per l'oggetto di riepilogo è anche impostato:
- (void) createSummary:(NSManagedObject*)employee
{
NSManagedObject* department = [employee valueForKey:@"department"];
NSManagedObject* summary =
[NSEntityDescription
insertNewObjectForEntityForName:@"DepartmentSummary"
inManagedObjectContext:[self managedObjectContext]];
[summary setValue:department forKey:@"department"];
NSAssert([department valueForKey:@"summary"] == summary,
@"Inverse relation not set");
}
Questa affermazione ha esito negativo. In effetti, se il risultato della stampa gli oggetti grandi e Info reparto della sintesi è stato impostato, otteniamo
entity: DepartmentSummary;
id: ..DepartmentSummary/..AA14> ;
data: {
department = "..Department/..AA13>";
}
per la sintesi, come previsto, ma
entity: Department;
id: ..Department/..AA13> ;
data: {
employee = "..Employee/..AA12>";
summary = nil;
}
per il reparto (con un nil
sommario). Se invece ritardiamo la chiamata a createSummary
in modo che non viene eseguito fino alla prossima iterazione del runloop:
- (void) observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
[self performSelector:@selector(createSummary:)
withObject:object
afterDelay:0];
}
allora tutto funziona come previsto.
Ritardare l'asserzione invece fa non aiuto: la relazione inversa realtà non c'è impostare nel grafico oggetto, anche se lo fa arrivare impostato nel database (se si dovesse salvare il database, e riavviare l'app, ora tutto ad un tratto appare la relazione inversa).
Si tratta di un bug nei dati principali? Questo comportamento documentato mi è sfuggito? Sto utilizzando i dati di base in modi che non erano previsti?
Nota che il gestore KVO viene chiamato mentre Core Data è (automaticamente) la fissazione di un (altro) inversa: abbiamo impostato manualmente il campo del reparto employee
, Core Data imposta automaticamente campo department
del dipendente, e che a sua volta innesca la Gestore KVO. Forse è troppo per gestire Core Data :) Infatti, quando impostiamo
[employee setValue:department forKey:@"department"];
invece, tutto funziona di nuovo come previsto.
Qualsiasi suggerimento sarebbe apprezzato.
Cosa succede se si imposta immediatamente il riepilogo, ma si ritarda la * assertion * fino al prossimo runloop? – jtbandes
Ottima domanda. Modificherò la domanda per rispondere - in sostanza, ritardare l'asserzione non aiuta. – edsko
ciao, ho notato lo stesso, funzionava prima ... – RolandasR