Note that there are some explanatory texts on larger screens.

plurals
  1. POInverse relation not set (in KVO handler)
    primarykey
    data
    text
    <p>I have a very strange problem with inverse relations in Core Data, and I have managed to reduce my problem to a minimal example, starting from a new project in xcode based on the window template with support for Core Data (i.e., there's very little there). </p> <p>Suppose we have a Core Data model with three entities: Department, Employee, and DepartmentSummary (some sort of entity representing some statistics about the department). For simplicity's sake, we have only one-to-one relations:</p> <pre><code>DepartmentSummary Department Employee --------------------------------------------------------- employee &lt;----&gt; department department &lt;----&gt; summary </code></pre> <p>This is all there is in the model. In <code>application:didFinishLaunchingWithOptions:</code> we create an employee and a department and set up KVO:</p> <pre><code>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"]; </code></pre> <p>The purpose of the KVO handler is to create a summary for the department as soon as the employee's department is set:</p> <pre><code>- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { [self createSummary:object]; } </code></pre> <p><code>createSummary</code> is simple: it creates a new summary object and associates it with the department, and <em>then checks that the inverse relation from the department to the summary object is also set</em>:</p> <pre><code>- (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"); } </code></pre> <p><em>This assertion fails.</em> Indeed, if we print the department and summary objects after the summary's department has been set, we get</p> <pre><code>entity: DepartmentSummary; id: ..DepartmentSummary/..AA14&gt; ; data: { department = "..Department/..AA13&gt;"; } </code></pre> <p>for the summary, as expected, but</p> <pre><code>entity: Department; id: ..Department/..AA13&gt; ; data: { employee = "..Employee/..AA12&gt;"; summary = nil; } </code></pre> <p>for the department (with a <code>nil</code> summary). If however we delay the call to <code>createSummary</code> so that it doesn't run until the next iteration of the runloop:</p> <pre><code>- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { [self performSelector:@selector(createSummary:) withObject:object afterDelay:0]; } </code></pre> <p>then everything works as expected. </p> <p>Delaying the <em>assertion</em> instead does <em>not</em> help: the inverse relation really does not get set <em>in the object graph</em>, though it does get set in the database (if you were to save the database, and restart the app, now all of a sudden the inverse relation appears).</p> <p>Is this a bug in Core Data? Is this documented behaviour which I have missed? Am I using Core Data in ways it was not intended? </p> <p>Note that the KVO handler gets called <em>while Core Data is (automatically) setting an(other) inverse</em>: we manually set the department's <code>employee</code> field, Core Data automatically sets the employee's <code>department</code> field, and that in turn triggers the KVO handler. Perhaps that is just too much for Core Data to handle :) Indeed, when we set</p> <pre><code>[employee setValue:department forKey:@"department"]; </code></pre> <p>instead, everything again works as expected.</p> <p>Any pointers would be appreciated. </p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload