Уникальность Core Data не работает?

У меня возникают проблемы с повторяющимися объектами одного и того же объекта в одном контексте при использовании двух контекстов управляемых объектов.

Рассмотрим следующий код:

[childMOC performBlockAndWait:^{

    // CREATE PERSON IN CHILD MOC
    Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" 
                                                   inManagedObjectContext:childMOC];

    person.name = @"Ben";

    // SAVE CHILD MOC TO PUSH CHANGES TO MAIN MOC
    [childMOC save:nil];

    NSManagedObjectID *personID = person.objectID;

    [mainMOC performBlockAndWait:^{
        // SAVE MAIN MOC TO PERSIST THE PERSON AND REPLACE ID TO PERMANENT
        [mainMOC save:nil];

        // GET THE PERSON IN THE MAIN MOC
        Person *personInMainContext = (Person*)[mainMOC objectWithID:personID];

        // GET THE PERSON'S NEW PERMANENT ID
        NSManagedObjectID *personIdAfterSaveToPersistentStore = personInMainContext.objectID;

        [childMOC performBlockAndWait:^{

            // GET THE PERSON IN THE CHILD MOC WITH ITS NEW PERMANENT ID
            // (this is common when sending the id from mainMOC to childMOC)
            Person *samePersonFetchedFresh = (Person*)[childMOC objectWithID:personIdAfterSaveToPersistentStore];

            // THE PERSON OBJECTS SHOULD BE EXACTLY THE SAME BECAUSE THE MOC GUARANTEES UNIQUING
            samePersonFetchedFresh.name = @"Jerry";
            NSLog(@"%@ & %@", person.name, samePersonFetchedFresh.name);

            // OUTPUT: Ben & Jerry
            // NOT THE SAME?!
        }];
    }];
}];

Это означает, что объект, созданный в дочернем MOC, теряет свою уникальную способность при сохранении в основном MOC/постоянном хранилище.

Может ли кто-нибудь объяснить, почему uniquing не работает в этой ситуации?


person thejaz    schedule 28.06.2012    source источник
comment
Я попробую перенести изменения из самого внутреннего блока в основной (сохранить основной блок). Тогда, я думаю, вы увидите уникальное поведение.   -  person Lorenzo B    schedule 28.06.2012
comment
Уникальное поведение уже нарушено, так как я получил два объекта, ссылающихся на один и тот же объект. Значит проблема уже случилась.   -  person thejaz    schedule 28.06.2012
comment
Привет thejaz, тебе удалось решить проблему?   -  person Dmitry Makarenko    schedule 10.07.2012
comment
Нет, не в хорошем смысле. Использование NSManagedObjectContextDidSaveNotification для решения проблемы, но это не идеально.   -  person thejaz    schedule 12.07.2012


Ответы (3)


Попробуйте обновить своего человека, прежде чем регистрировать его имя. Я думаю, что unique работает, но инвалидация кеша может не работать.

person svena    schedule 28.06.2012
comment
Я знаю, что он напечатает Джерри и Джерри, если я обновлю объект человека перед печатью в журнале, но тот факт, что есть два разных объекта, означает, что уникальность не работает. Даже если я обновлю, у человека все еще будет временный идентификатор, а у samePersonFetchedFresh — постоянный. Это тоже странно, разве он не должен обновлять временный до постоянного при обновлении? - person thejaz; 28.06.2012
comment
Ну, может, но он не предназначен для этого. Так как изменения с вложенными контекстами распространяются только из дочернего -> родительского контекста. Разработчик должен обновить дочерний контекст, если есть изменения непосредственно в родительском контексте. - person svena; 28.06.2012
comment
Но это то, что я делаю, когда звоню, чтобы обновить объект, и даже в этом случае он не обновляет идентификатор. - person thejaz; 28.06.2012
comment
Ах, вы имеете в виду, что временный идентификатор сохраняется при обновлении, я понимаю вашу точку зрения. Хотя вы никогда не должны ссылаться на объекты Core Data извне по их временному идентификатору, поскольку они просто... временные. - person svena; 28.06.2012
comment
Однако давайте проясним одну вещь. В любой момент времени по-прежнему существует один объект, хотя одна из ссылок показывает устаревший временный идентификатор. Интересно, если бы вы снова попытались получить к нему доступ с помощью этого временного идентификатора, получилось бы это успешно? Сомневаюсь, но это возможно. Так что в этом смысле unique работает. Просто вы не можете полагаться на objectID исключительно для проверки дубликатов, если вы передаете их таким образом. - person svena; 28.06.2012
comment
Нет, в любой момент времени существует не один объект, а два. И в этом проблема, внезапно я использовал старый объект, отношения которого не были обновлены, потому что я не получил единственный уникальный объект. - person thejaz; 28.06.2012
comment
Если это правда, то это точно баг. - person svena; 28.06.2012

Где-то вы должны слушать основной контекст NSManagedObjectContextDidSaveNotification и распространять изменения на дочерний контекст(ы) с помощью mergeChangesFromContextDidSaveNotification.

Если полное слияние вам не подходит, вы также можете обновить каждую сущность по отдельности с помощью refreshObject:mergeChanges:.

Так или иначе, вам придется распространять изменения, если вы не хотите иметь дело с актуальными экземплярами и старыми копиями.

Это не делается автоматически, и это хорошо, так как операция может быть довольно тяжелой. Вы должны обрабатывать слияния, когда это необходимо, для соответствующих контекстов. Например, если вы заполняете свою базу данных с помощью какой-либо службы JSON, вы будете использовать контекст для добавления сущностей в базу данных, и вы захотите обновить пользовательский интерфейс, когда данные согласуются, а не каждый раз, когда вы сохраняете что-то в основной контекст и, возможно, на диск. Точно так же этот контекст, безусловно, не заинтересован в изменениях, внесенных в пользовательский интерфейс, а затем сохраненных в основном контексте/диске. Итак, процесс не является автоматическим.

person fabrice truillot de chambrier    schedule 28.06.2012
comment
Это не относится к новым типам параллелизма и вложенным контекстам управляемых объектов, представленным в iOS5. - person svena; 28.06.2012
comment
Я уже делаю это сейчас, чтобы решить проблему с повторяющимися объектами. Но все объекты, измененные/добавленные в childMOC, которые я переместил в mainMOC путем сохранения, из-за этого вернутся обратно и будут повторно загружены в childMOC. Вот почему я не хочу этого делать, и я действительно хочу, чтобы уникальность работала, чтобы я мог удалить это решение NSManagedObjectContextDidSaveNotification + mergeChangesFromContextDidSaveNotification. - person thejaz; 28.06.2012
comment
Добавил слово о refreshObject:mergeChanges: - person fabrice truillot de chambrier; 28.06.2012

Мое решение заключалось в следующем:

1) Используйте фоновый MOC в качестве родительского MOC и основной MOC в качестве дочернего. В качестве бонуса мне не нужно сохранять основной MOC, чтобы получить постоянные идентификаторы.

[DC.backgroundMOC performBlock:^{
    // Add, save and update managed objects
    [DC saveContext]; // The changes is being pushed to the main context
}];

2) Используйте NSManagedObjectContextDidSaveNotification, чтобы поддерживать основной MOC в актуальном состоянии (основной MOC обновляет пользовательский интерфейс)

- (void) backgroundMOCSaved:(NSNotification*)notification {
    [mainMOC performBlock:^{
        [mainMOC mergeChangesFromContextDidSaveNotification:notification];
    }];
}
person thejaz    schedule 29.07.2012