У меня очень странная проблема с обратными отношениями в Core Data, и мне удалось свести мою проблему к минимальному примеру, начиная с нового проекта в xcode на основе шаблона окна с поддержкой для Core Data (то есть там очень мало).
Предположим, у нас есть модель Core Data с тремя сущностями: Department, Employee и DepartmentSummary (своего рода сущность, представляющая некоторую статистику об отделе). Для простоты у нас есть только однозначные отношения:
DepartmentSummary Department Employee
---------------------------------------------------------
employee <----> department
department <----> summary
Это все, что есть в модели. В приложении : didFinishLaunchingWithOptions:
мы создаем сотрудника и отдел и настраиваем 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"];
Назначение обработчика KVO - создать сводку для отдела, как только отдел сотрудника установлен:
- (void) observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
[self createSummary:object];
}
createSummary
прост: он создает новый объект сводки и связывает его с отделом, а затем проверяет, что также установлено обратное отношение от отдела к объекту сводки :
- (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");
}
Это утверждение не выполняется. Действительно, если мы печатаем объекты отдела и сводки после того, как отдел сводки был установлен, мы получим
entity: DepartmentSummary;
id: ..DepartmentSummary/..AA14> ;
data: {
department = "..Department/..AA13>";
}
для сводки, как и ожидалось, но
entity: Department;
id: ..Department/..AA13> ;
data: {
employee = "..Employee/..AA12>";
summary = nil;
}
для отдела (с сводкой nil
). Однако если мы отложим вызов createSummary
, чтобы он не запустился до следующей итерации цикла выполнения:
- (void) observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
[self performSelector:@selector(createSummary:)
withObject:object
afterDelay:0];
}
, тогда все будет работать, как ожидалось.
Задержка утверждения вместо этого не помогает: обратное отношение действительно не устанавливается в графе объектов , хотя оно и устанавливается в базе данных (если бы вы сохранили базу данных и перезапустили приложение, теперь внезапно появится обратная зависимость.)
Это ошибка в Core Data? Это задокументированное поведение, которое я пропустил? Я использую Core Data не предназначенным для этого способом?
Обратите внимание, что обработчик KVO вызывается , в то время как Core Data (автоматически) устанавливает (другое) обратное : мы вручную устанавливаем поле сотрудника
отдела, Core Data автоматически устанавливает значение сотрудника отдел
, которое, в свою очередь, запускает обработчик KVO. Возможно, это слишком много для Core Data :) Действительно, когда мы устанавливаем
[employee setValue:department forKey:@"department"];
, все снова работает, как ожидалось.
Любые указатели будут оценены.